It is easy to declare a method, which takes a method name as a string:
public void DoSomethingWithMethodName(string methodName)
{
// Do something with the method name here.
}
and the call it as:
DoSomethingWithMethodName(nameof(SomeClass.SomeMethod));
I want to get rid of nameof and call some other method as:
DoSomethingWithMethod(SomeClass.SomeMethod);
and then be able to get the name of the method the same as in the example above. It "feels" possible to do that using some Expression and / or Func sorcery. The question is what signature this DoSomethingWithMethod should have and what it should actually do!
====================================
The question seems to cause a lot of confusion and the answers assume what I did not ask. Here is a hint at what I am aiming but cant' get right. This is for some different problem (for which I have a solution). I can declare:
private async Task CheckDictionary(Expression<Func<LookupDictionary>> property, int? expectedIndex = null)
{
await RunTest(async wb =>
{
var isFirst = true;
foreach (var variable in property.Compile().Invoke())
{
// Pass the override value for the first index.
await CheckSetLookupIndex(wb, GetPathOfProperty(property), variable, isFirst ? expectedIndex : null);
isFirst = false;
}
});
}
where GetPathOfProperty comes from:
https://www.automatetheplanet.com/get-property-names-using-lambda-expressions/
and Fully-qualified property name
and then use:
[Fact]
public async Task CommercialExcelRaterService_ShouldNotThrowOnNumberOfStories() =>
await CheckDictionary(() => EqNumberOfStories, 2);
where EqNumberOfStories is:
public static LookupDictionary EqNumberOfStories { get; } = new LookupDictionary(new Dictionary<int, string>
{
{ 1, "" },
{ 2, "1 to 8" },
{ 3, "9 to 20" },
{ 4, "Over 20" }
});
As you can see, I am passing a property and then "unwinding" it to get to the source. I'd like to do the same but in a simpler setup as described above.
You can use [CallerMemberName] to get the name of the calling method.
public void DoProcessing()
{
TraceMessage("Something happened.");
}
public void TraceMessage(string message,
[System.Runtime.CompilerServices.CallerMemberName] string memberName = "",
[System.Runtime.CompilerServices.CallerFilePath] string sourceFilePath = "",
[System.Runtime.CompilerServices.CallerLineNumber] int sourceLineNumber = 0)
{
System.Diagnostics.Trace.WriteLine("message: " + message);
System.Diagnostics.Trace.WriteLine("member name: " + memberName);
System.Diagnostics.Trace.WriteLine("source file path: " + sourceFilePath);
System.Diagnostics.Trace.WriteLine("source line number: " + sourceLineNumber);
}
In example above example memberName param will be assigned with value DoProcessing.
Sample output
message: Something happened.
member name: DoProcessing
source file path:
C:\Users\user\AppData\Local\Temp\LINQPad5_osjizlla\query_gzfqkl.cs
source line number: 37
https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute(v=vs.110).aspx
Basically what you have to do is declare the parameter as a Func that matches the method signature you want to accept and then wrap that in an Expression so that the compiler will give you an expression tree rather than an actual delegate. Then you can walk through the expression tree to find a MethodCallExpression from which you can get the method name. (BTW, the sample code in the link you provided will work with method calls too, just like you want, in addition to properties)
what signature this DoSomethingWithMethod should have
It depends on the signature of the method you would like to take as a parameter.
If some method looks like:
public MyReturnType SomeMethod(MyParameterType parameter) {}
Then the DoSomethingWithMethod signature would look like:
public void DoSomethingWithMethod(Expression<Func<MyParameterType,MyReturnType>> methodExpression) {}
You can also make it generic incase you want to accept methods with slightly different signatures (however if you want to accept methods with different numbers of parameters you will have to use overloading, also the C# compiler probably won't resolve the generic type parameters automatically in this situation, and you'll have to specify them explicitly)
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression) {}
and what it should actually do
I suppose this question is actually, how do I get the method name as a string from the expression tree?
There are a couple of different ways to do this and it depends on how robust you want your code to be. Consider that the above method signature allows for passing a delegate that is significantly more complicated than just a single method call. For example:
DoSomethingWithMethod(t => t.SomeMethod().SomeOtherMethod(5) + AnotherThing(t));
If you search the expression tree generated from the above you will find quite a few method calls not just one. If you just want to enforce that the passed parameter is a single method call, it's probably easier to just try to cast the expression Body property to a MethodCallExpression
public void DoSomethingWithMethod<TParam,TReturn>(Expression<Func<TParam,TReturn>> methodExpression)
{
if (methodExpression.Body is MethodCallExpression methodCall)
{
var methodName = methodCall.Method.Name;
//...
}
}
The other option is to use the visitor pattern, this is helpful especially if you have a more complex scenario, such as you would like to retrieve a list of all method names when there are multiple for example, or a support a mixture of property or method calls, etc.
For this option, create a class that inherits ExpressionVisitor and overrides the appropriate methods in the base class and store the results somewhere. Here's an example:
class MyVisitor : ExpressionVisitor
{
public List<string> Names { get; } = new List<string>();
protected override Expression VisitMember(MemberExpression node)
{
if(node.Member.MemberType == MemberTypes.Method)
{
Names.Add(node.Member.Name);
}
return base.VisitMember(node);
}
}
you can call it like this:
var visitor = new MyVisitor();
visitor.Visit(methodExpression.Body);
var methodName = visitor.Names[0];
//...
Finally, to call it, you won't be able to use the shortened "method group" mode of calling DoSomethingWithMethod since C# compiler is not capable of automatically converting a method group to an expression tree (it can however automatically convert it to a regular delegate, which is the notation you are used to).
So you can't do:
DoSomethingWithMethod(SomeMethod);
instead it will have to look like a lambda expression:
DoSomethingWithMethod(t => SomeMethod(t));
or if no parameters:
DoSomethingWithMethod(() => SomeMethod());
Related
Due to performance reasons GitHub page suggests "If you need to run the same expression multiple times with different parameters I suggest to parse it one time and then invoke the parsed expression multiple times."
I have a class like this:
public class Conditional : TemplateElement
{
public string Condition { get => _condition; set => _condition = value; }
public Lambda Parsed { get => ...; private set => ... }
public Conditional(string condition)
{
Condition = condition;
...
ParseExpression();
}
private void ParseExpression()
{
var target = new Interpreter();
Lambda = target.Parse(Condition, ???);
}
}
The 'Condition' string can be in form:
item["CreatedDate"] <= DateTime.Today.AddDays(-2)
Now, at the moment of instantiation of Conditional class I don't know what the 'item' contains, I want it to be parsed so I can use it later. Lambda should resolve to the Condition to boolean.
I'm not exactly sure how to achieve this, documentation doesn't help me much. Should I define 'item' as specific type in Parameters array?
I want to write an extension methods for compare some proprties of two objects. I wrote this code:
public static void AreTwoObjectsEqual(this Assert asr, object Actual, object Expected, List<string> FieldsMustCheck)
{
foreach (string item in FieldsMustCheck)
{
if (Actual.GetType().GetProperty(item) == null || Expected.GetType().GetProperty(item) == null)
{
throw new Exception("Property with name : " + item + " not found in objects ");
}
var ActualPropertyValue = Actual.GetType().GetProperty(item).GetValue(Actual, null);
var ExpectedPropertyValue = Expected.GetType().GetProperty(item).GetValue(Expected, null);
if (ActualPropertyValue != ExpectedPropertyValue)
{
throw new AssertFailedException("Test failed for propery : " + item);
}
}
}
when I want to build the project I get this error:
'Microsoft.VisualStudio.TestTools.UnitTesting.Assert': static types cannot be used as parameters
Can any one help me remove this error.Thanks
Well the compiler error message is fairly clear: Assert is a static class, so you can't use that as the parameter type for the extension method. It's not clear why you wanted to in the first place, to be honest. If you were hoping to be able to use Assert.AreTwoObjectsEqual, you just can't do that - extension methods are meant to mimic instance methods, not static methods in a different type.
I suspect you should just create a static class of your own, e.g. MoreAssert, and just make it a normal static method:
public static class MoreAssert
{
public static void AreEqualByProperties(object expected, object actual,
List<string> propertyNames)
{
...
}
}
Parameter names changed to comply with .NET naming conventions. I'd strongly encourage you to use camelCase names for local variables, too. I've also reordered the parameters to be consistent with the other assertions.
So then you'd just call:
MoreAssert.AreEqualByProperties(...);
You might also consider using params string[] propertyNames instead of List<string> propertyNames to make it easier to call.
I'm sure there's an answer already on the forum somewhere, but I haven't been able to find it so far. As per this example I am using anonymous methods in conjunction with a delegate in order to have different methods with the different parameters but the same return type all work as function parameters:
public delegate TestCaseResult Action();
...
[TestDescription("Test whether the target computer has teaming configured")]
public TestCaseResult TargetHasOneTeam()
{
// do some logic here and return
// TestCaseResult
}
[TestDescription("Test whether the target computer has the named team configured")]
public TestCaseResult TargetHasNamedTeam(string teamName)
{
// do some logic here and return
// TestCaseResult
}
...
public static void TestThat(TestCaseBase.Action action)
{
TestCaseResult result = action.Invoke();
// I want to get the value of the TestDescription attribute here
}
...
// usage
TestThat(() => TargetHasOneTeam());
TestThat(() => TargetHasNamedTeam("Adapter5"));
As you can see from the example, I'd really like to be able to get the TestDescriptionAttribute attribute from within the TestThat() function. I've already poked through the Action parameter which contains my method but haven't been able to "find" my TargetHasOneTeam() method.
If you change TestThat(() => TargetHasOneTeam()) (you wrapping your delegate into another action) to TestThat(TargetHasOneTeam) and change TestThat like this:
public static void TestThat(TestCaseBase.Action action)
{
TestCaseResult result = action.Invoke();
var attrs = action.GetInvocationList()[0].Method.GetCustomAttributes(true);
// I want to get the value of the TestDescription attribute here
}
will give you what you need.
With expressions:
public static void TestThat(Expression<Func<TestResult>> action)
{
var attrs = ((MethodCallExpression)action.Body).Method.GetCustomAttributes(true);
var result = action.Compile()();
}
In this particular case it's essentially inaccessible. You are creating a lambda which executes the method in question. That lambda eventually results in a new method being generated which is eventually the argument of the Action delegate. That method has no relation to TargetHasOneTeam and is only apparently if you dig through the IL instructions in the body.
You could skip the lambda and do a method group conversion here.
TestThat(TargetHasOneTeam);
Now TargetHasOneTeam is being directly assigned to the delegate instance and would be visible in the Delegate::MethodInfo property.
Note: In general though this is a bad idea for precisely the problem you're encountering. The attributes on the method shouldn't affect it's ability to satisfy the delegate instantiation. I would avoid this type of inspection if possible.
You can get the attribute of any member with Attribute.GetCustomAttribute. First check that the attribute is defined. For example:
public static void TestThat(TestCaseBase.Action action)
{
TestCaseResult result = action.Invoke();
if(System.Attribute.IsDefined(action.Method, typeof(TestDescriptionAttribute)))
{
var attribute = (TestDescriptionAttribute)System.Attribute.GetCustomAttribute(action.Method,
typeof(TestDescriptionAttribute));
Console.WriteLine(attribute.TestDescription);
}
}
See this example on MSDN.
class TestAuthorAttribute
{
static void Test()
{
PrintAuthorInfo(typeof(FirstClass));
PrintAuthorInfo(typeof(SecondClass));
PrintAuthorInfo(typeof(ThirdClass));
}
private static void PrintAuthorInfo(System.Type t)
{
System.Console.WriteLine("Author information for {0}", t);
// Using reflection.
System.Attribute[] attrs = System.Attribute.GetCustomAttributes(t); // Reflection.
// Displaying output.
foreach (System.Attribute attr in attrs)
{
if (attr is Author)
{
Author a = (Author)attr;
System.Console.WriteLine(" {0}, version {1:f}", a.GetName(), a.version);
}
}
}
I'd like to know whether I can retrieve an argument name from an argument value. I need this functionality because I've build myself a static class (called Requires) to make argument checking in method bodies a one-liner. Currently, the validation methods are implemented like this:
Requires.StringNotNullOrEmpty(string argName, string argValue) {...}
To validate an argument, you have to provide the name of the argument (later used to throw a meaningful ArgumentException) and its value.
My question is, is there a way to retrieve the argument name from an argument value inside a method body?
Thanks in advance and happy easter!
I think you are looking for Reflection.
Reflection: How to Invoke Method with parameters
class A
{
public void MyMethod(int num, string aString)
{
ParameterInfo[] parameters = typeof(A).GetMethod("MyMethod", BindingFlags.Public|BindingFlags.Instance).GetParameters();
string secondParameterName = parameters[1].Name; //you will get aString
}
}
No, you cannot know the name used by the calling code - because in many cases, what's been passed to your method doesn't have a name at all, e.g. it could be an expression, or a literal. So there's no general solution to this.
No. Point. You know the name of the arbument (argName). You can not know what is was SET from because... that is not even part of the argument. It is part of the knowledge of the outer class, not the argument (which would return argName).
Not sure if this is what you had in mind
internal class TestClass
{
private void DoSomething(string myArg)
{
// returns the name of the argument = "myArg"
string myArgName = GetArgumentName(() => myArg);
// check
System.Diagnostics.Debug.Assert(string.Compare("myArg", myArgName, System.StringComparison.InvariantCulture) == 0, "names do not match");
}
private static string GetArgumentName<T>(System.Linq.Expressions.Expression<System.Func<T>> argument)
{
string argumentName = null;
System.Linq.Expressions.MemberExpression body = (System.Linq.Expressions.MemberExpression)argument.Body;
if (body.Member != null)
{
argumentName = body.Member.Name;
}
if (argumentName == null)
{
// could not retrieve argument name
}
return argumentName;
}
}
I have a scenario where I have to get an array of strings that represent each of the property names used within a Func parameter. Here is an example implementation:
public class CustomClass<TSource>
{
public string[] GetPropertiesUsed
{
get
{
// do magical parsing based upon parameter passed into CustomMethod
}
}
public void CustomMethod(Func<TSource, object> method)
{
// do stuff
}
}
Here would be an example usage:
var customClass = new CustomClass<Person>();
customClass.CustomMethod(src => "(" + src.AreaCode + ") " + src.Phone);
...
var propertiesUsed = customClass.GetPropertiesUsed;
// propertiesUsed should contain ["AreaCode", "Phone"]
The part I'm stuck on in the above is the "do magical parsing based upon parameter passed into CustomMethod."
You should use Expression<Func<>> class instead. The expression contains actual tree, and can easily be complied to get a delegate (which is a func). What You are really trying to do is to look at the body of the expression and reason about it. Expression class provides You with all the neccessary infrastructure.
You'll need to change your CustomMethod to take an Expression<Func<TSource, object>>, and probably subclass the ExpressionVisitor, overriding VisitMember:
public void CustomMethod(Expression<Func<TSource, object>> method)
{
PropertyFinder lister = new PropertyFinder();
properties = lister.Parse((Expression) expr);
}
// this will be what you want to return from GetPropertiesUsed
List<string> properties;
public class PropertyFinder : ExpressionVisitor
{
public List<string> Parse(Expression expression)
{
properties.Clear();
Visit(expression);
return properties;
}
List<string> properties = new List<string>();
protected override Expression VisitMember(MemberExpression m)
{
// look at m to see what the property name is and add it to properties
... code here ...
// then return the result of ExpressionVisitor.VisitMember
return base.VisitMember(m);
}
}
This should get you started in the right direction. Let me know if you need some help figuring out the "... code here ..." part.
Useful links:
Expression Trees
How to Modify Expression Trees
ExpressionVisitor
VisitMember
MemberExpression