Checking a MethodInfo against a delegate - c#

How can I determine if a MethodInfo fits a distinct Delegate Type?
bool IsMyDelegate(MethodInfo method);
Edit:
I'm given a MethodInfo object and want to know if it fits the delegate interface.
Apart from the obvious
private bool IsValidationDelegate(MethodInfo method)
{
var result = false;
var parameters = method.GetParameters();
if (parameters.Length == 2 &&
parameters[0].ParameterType == typeof(MyObject1) &&
parameters[1].ParameterType == typeof(MyObject2) &&
method.ReturnType == typeof(bool))
{
result = true;
}
else
{
m_Log.Error("Validator:IsValidationDelegate", "Method [...] is not a ValidationDelegate.");
}
return result;
}

If method is a static method:
bool isMyDelegate =
(Delegate.CreateDelegate(typeof(MyDelegate), method, false) != null);
If method is an instance method:
bool isMyDelegate =
(Delegate.CreateDelegate(typeof(MyDelegate), someObj, method, false) != null)
(Unfortunately you need an instance in this case because Delegate.CreateDelegate is going to try to bind a delegate instance, even though in this case all we care about it whether the delegate could be created or not.)
In both cases, the trick is basically to ask .NET to create a delegate of the desired type from the MethodInfo, but to return null rather than throwing an exception if the method has the wrong signature. Then testing against null tells us whether the delegate had the right signature or not.
Note that because this actually tries to create the delegate it will also handle all the delegate variance rules for you (e.g. when the method return type is compatible but not exactly the same as the delegate return type).

Related

Identify Func<> parameter in method using reflection

I have a collection of methods, and I'd like to identify any that contain a Func<(,,,)> parameter (ideally through reflection).
This would be simple enough by tagging such parameters with [IsFuncAttribute], but I'd like to avoid that approach if possible.
For example, if I had the following method, how could I reliably determine that the third parameter is a Func<,>?
public T MyMethod<T>(T param1, bool param2, Func<t,bool> param3)
{
// do something
}
Alternatively, being able to identify the third parameter as a delegate with a non void return type would be equally useful.
MethodInfo methodInfo = ...;
ParameterInfo[] parameters = methodInfo.GetParameters();
bool thirdParameterIsFunc =
parameters.Length >= 3 &&
parameters[2].ParameterType.IsGenericType &&
parameters[2].ParameterType.GetGenericTypeDefinition() == typeof(Func<,>));
DotNetFiddle
This is specifically for Func<,>. If you want to match any sort of Func, with any number of parameters, then you'll either need a list of typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), etc, or you'll need to match on the full name of the type.
If you need to match a Func with any number of parameters, as an alternative to explicitly testing against every version of Func (There are 17 of them), you could use something like this extension method (Note that this particular example is specific to .NET Core/.Net 5.0):
public static bool IsAnyFunc(this Type type)
{
int typeParameterCount = type.GenericTypeArguments.Length;
if (typeParameterCount == 0)
{
return false;
}
Type funcType = typeof(Func<>)
.Assembly
.GetTypes()
.FirstOrDefault(t =>
t.Name.StartsWith("Func`")
&& t.GetTypeInfo().GenericTypeParameters.Length == typeParameterCount);
return funcType == null ? false : type.GetGenericTypeDefinition() == funcType;
}
Edit:
Although, if you're going to be making this check frequently, the above may not be performant enough. In which case you may want to explicitly list every possilbe Func. However, you don't actually need to check against them all, if you index them by parameter count:
private static readonly List<Type> FuncTypes = new()
{
typeof(Func<>),
typeof(Func<,>),
typeof(Func<,,>),
typeof(Func<,,,>),
typeof(Func<,,,,>),
typeof(Func<,,,,,>),
typeof(Func<,,,,,,>),
typeof(Func<,,,,,,,>),
typeof(Func<,,,,,,,,>),
typeof(Func<,,,,,,,,,>),
typeof(Func<,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,,,,,>),
typeof(Func<,,,,,,,,,,,,,,,,>)
};
public static bool IsAnyFunc(this Type type)
{
int typeParameterCount = type.GenericTypeArguments.Length;
if (typeParameterCount < 1 || typeParameterCount > FuncTypes.Count)
{
return false;
}
return type.GetGenericTypeDefinition() == FuncTypes[typeParameterCount - 1];
}

How to build expression tree for Contains<T>

I'm following this SO answer to convert lambda expressions to partial SQL syntax.
However, I have problems parsing the expression for Contains. I added a method:
private bool ParseContainsExpression(MethodCallExpression expression)
{
MemberExpression member = (MemberExpression)expression.Arguments[0];
var methodInfo = typeof(List<int>).GetMethod("Contains", new Type[] { typeof(int) });
//TODO check if list contains value
return false;
}
As I'm totally new to expressions, I don't know from where get the property name and value, and the list that contains the values I want to check against. Where are these properties and values stored within the expression?
Your implementation is quite different from the example answer. You really need to inherit from ExpressionVisitor so that you can properly parse the tree.
Let's take this expression for an example:
var myList = new List<string> { "A" };
Expression<Func<string, bool>> a = (s) => myList.Contains(s);
ParseContainsExpression(a.Body as MethodCallExpression);
private bool ParseContainsExpression(MethodCallExpression expression)
{
expression.Object; //myList
expression.Arguments[0]; //s
return false;
}
Note however, these are still Expressions, they are not actual values yet. You need to invoke the expression to get the values.
However, in our case - myList is actually a ConstantExpression. So we can do this:
((expression.Object as MemberExpression).Expression as ConstantExpression).Value; //myList
Which returns us the original list. Note that it's a field access, because the expression is compiled into a closure, which puts myList as a field in the closure class. As you can see, we have to make a lot of assumptions as to the type of Expression we're handling (that it's a field access and then a constant expression). You really need a fully-fledged visitor implementation to do this properly (which the linked answer describes)
Basically you need to process the Method, Object(expression that represents the instance for instance method calls or null for static method calls) and Arguments(a collection of expressions that represent arguments of the called method) properties of the MethodCallExpression class.
Specifically for Contains, you need to avoid (or process differently if needed) the string.Contains method, and also handle static methods like Enumerable.Contains as well as instance methods like ICollection<T>.Contains, List<T>.Contains etc. In order to get the list values (when possible), you have to find some sort of a constant expression.
Here is a sample:
private bool ParseContainsExpression(MethodCallExpression expression)
{
// The method must be called Contains and must return bool
if (expression.Method.Name != "Contains" || expression.Method.ReturnType != typeof(bool)) return false;
var list = expression.Object;
Expression operand;
if (list == null)
{
// Static method
// Must be Enumerable.Contains(source, item)
if (expression.Method.DeclaringType != typeof(Enumerable) || expression.Arguments.Count != 2) return false;
list = expression.Arguments[0];
operand = expression.Arguments[1];
}
else
{
// Instance method
// Exclude string.Contains
if (list.Type == typeof(string)) return false;
// Must have a single argument
if (expression.Arguments.Count != 1) return false;
operand = expression.Arguments[0];
// The list must be IEnumerable<operand.Type>
if (!typeof(IEnumerable<>).MakeGenericType(operand.Type).IsAssignableFrom(list.Type)) return false;
}
// Try getting the list items
object listValue;
if (list.NodeType == ExpressionType.Constant)
// from constant value
listValue = ((ConstantExpression)list).Value;
else
{
// from constant value property/field
var listMember = list as MemberExpression;
if (listMember == null) return false;
var listOwner = listMember.Expression as ConstantExpression;
if (listOwner == null) return false;
var listProperty = listMember.Member as PropertyInfo;
listValue = listProperty != null ? listProperty.GetValue(listOwner.Value) : ((FieldInfo)listMember.Member).GetValue(listOwner.Value);
}
var listItems = listValue as System.Collections.IEnumerable;
if (listItems == null) return false;
// Do whatever you like with listItems
return true;
}

how to cmpare same types where IsGenericTypeDefinition is deff

I have a issue where I want to identify if a object is of type KeyValuePair<,>
when I compare in if:
else if (item.GetType() == typeof(KeyValuePair<,>))
{
var key = item.GetType().GetProperty("Key");
var value = item.GetType().GetProperty("Value");
var keyObj = key.GetValue(item, null);
var valueObj = value.GetValue(item, null);
...
}
this is false as IsGenericTypeDefinition is different for them.
Can someone explain me why this is happening and how to solve the this issue in correct way (I mean not comparing Names or other trivial fields.)
THX in advance!
item.GetType() == typeof(KeyValuePair<,>)
The above will never work: it is impossible to make an object of type KeyValuePair<,>.
The reason is that typeof(KeyValuePair<,>) does not represent a type. Rather, it is a generic type definition - a System.Type object used to examine structures of other generic types, but not themselves representing a valid .NET type.
If an item is, say, a KeyValuePair<string,int>, then item.GetGenericTypeDefintion() == typeof(KeyValuePair<,>)
Here is how you can modify your code:
...
else if (item.IsGenericType() && item.GetGenericTypeDefintion() == typeof(KeyValuePair<,>)) {
...
}
Found this piece of code, give it a try:
public bool IsKeyValuePair(object o)
{
Type type = o.GetType();
if (type.IsGenericType)
{
return type.GetGenericTypeDefinition() != null ? type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>) : false;
}
return false;
}
Source:
http://social.msdn.microsoft.com/Forums/hu-HU/csharpgeneral/thread/9ad76a19-ed9c-4a02-be6b-95870af0e10b

Call a function with unknown type and number of parameters in C#

I am writing a library in C# and I have to call some methods defined by other programmers in their classes, so I do not priory know about types and number of parameters. For example,
Class exampleClass{
void method1(int param1, double param2){...}
bool method2(){...}
object method3(string param1){....}
}
In my program, I want to call these methods. As I don't know their parameters and return types, I cannot use "delegate"s (which have known types and parameters) but in run-time, I can use, for example, reflection to extract methods and their parameters ("MethodInfo"s) from the class but how to use this information to call the methods? (assuming that I can generate the proper values to be used as the parameters of methods).
Thanks
PS: I know "params object []" approach but it will force programmers to use "params" objects instead of defining their usual parameters in their methods. So, I don't want to use this approach.
You can use reflection to get all the information you need about a method.
For example once you have the MethodInfo you can get the ReturnType
Type MyType = Type.GetType("System.Reflection.FieldInfo");
MethodInfo Mymethodinfo = MyType.GetMethod("GetValue");
Console.Write ("\n" + MyType.FullName + "." + Mymethodinfo.Name);
Console.Write ("\nReturnType = {0}", Mymethodinfo.ReturnType);
GetParameters will tell you the parameters:
Type delegateType = typeof(MainClass).GetEvent("ev").EventHandlerType;
MethodInfo invoke = delegateType.GetMethod("Invoke");
ParameterInfo[] pars = invoke.GetParameters();
foreach (ParameterInfo p in pars)
{
Console.WriteLine(p.ParameterType);
}
Once you have this information you can use Invoke to actually call the method.
Its first argument is the "object on which to invoke the method".
Its second argument is the argument list for said method.
The code I'm providing works with the ExampleClass you provided, but in no way is it a complete solution. You need to take generics, ref, out params, and probably a whole lot of other things into consideration.
public void CallAllMethods(object instance)
{
foreach (MethodInfo method in instance.GetType().GetMethods())
{
if (method.IsGenericMethod || method.DeclaringType == typeof(object))
{
// skipping, System.Object method or a generic method
continue;
}
var defaultParamValues = method.GetParameters().Select(p => GetDefaultValue(p.ParameterType)).ToArray();
Console.WriteLine("Invoking {0} with param values {1}", method.Name, string.Join(", ", defaultParamValues));
object retVal = method.Invoke(instance, defaultParamValues);
if (method.ReturnType != typeof(void))
{
Console.WriteLine(" and returned a value of {0}", retVal);
}
}
}
public static object GetDefaultValue(Type type)
{
return type.IsValueType ? Activator.CreateInstance(type) : null;
}

How do i know when a lambda expression is null

I need to programatically check whether a nested property/function result in a lambda expression is null or not. The problem is that the null could be in any of the nested subproperties.
Example. Function is:
public static bool HasNull<T, Y>(this T someType, Expression<Func<T, Y>> input)
{
//Determine if expression has a null property
}
Use:
person.HasNull(d=>d.addressdetails.Street)
person.HasNull(d=>d.addressdetails[1].Street)
person.HasNull(d=>d.addressdetails.FirstOrDefault().Street)
person.HasNull(d=>d.InvoiceList.FirstOrDefault().Product.Name)
In any of the examples addressdetails or street, or invoicelist or the product or the name could be null.The code will throw an exception if i try to invoke the function and some nested property is null.
Important: I don't want to use a try catch for this because that is desastrous for the debugging performance.
The reason for this approach is to quickly check for values while i don't want to forget any nulls and so cause exceptions. This is handy for reporting solutions and grids where a null on the report can just show empty and has no futher business rules.
related post: Don't stop debugger at THAT exception when it's thrown and caught
It is possible, but I'm not sure I would recommand it. Here is something that you may find usefull: it doesn't return a boolean, but instead, the leaf value of the expression if possible (no null reference).
public static class Dereferencer
{
private static readonly MethodInfo safeDereferenceMethodInfo
= typeof (Dereferencer).GetMethod("SafeDereferenceHelper", BindingFlags.NonPublic| BindingFlags.Static);
private static TMember SafeDereferenceHelper<TTarget, TMember>(TTarget target,
Func<TTarget, TMember> walker)
{
return target == null ? default(TMember) : walker(target);
}
public static TMember SafeDereference<TTarget, TMember>(this TTarget target, Expression<Func<TTarget, TMember>> expression)
{
var lambdaExpression = expression as LambdaExpression;
if (lambdaExpression == null)
return default(TMember);
var methodCalls = new Queue<MethodCallExpression>();
VisitExpression(expression.Body, methodCalls);
var callChain = methodCalls.Count == 0 ? expression.Body : CombineMethodCalls(methodCalls);
var exp = Expression.Lambda(typeof (Func<TTarget, TMember>), callChain, lambdaExpression.Parameters);
var safeEvaluator = (Func<TTarget, TMember>) exp.Compile();
return safeEvaluator(target);
}
private static Expression CombineMethodCalls(Queue<MethodCallExpression> methodCallExpressions)
{
var callChain = methodCallExpressions.Dequeue();
if (methodCallExpressions.Count == 0)
return callChain;
return Expression.Call(callChain.Method,
CombineMethodCalls(methodCallExpressions),
callChain.Arguments[1]);
}
private static MethodCallExpression GenerateSafeDereferenceCall(Type targetType,
Type memberType,
Expression target,
Func<ParameterExpression, Expression> bodyBuilder)
{
var methodInfo = safeDereferenceMethodInfo.MakeGenericMethod(targetType, memberType);
var lambdaType = typeof (Func<,>).MakeGenericType(targetType, memberType);
var lambdaParameterName = targetType.Name.ToLower();
var lambdaParameter = Expression.Parameter(targetType, lambdaParameterName);
var lambda = Expression.Lambda(lambdaType, bodyBuilder(lambdaParameter), lambdaParameter);
return Expression.Call(methodInfo, target, lambda);
}
private static void VisitExpression(Expression expression,
Queue<MethodCallExpression> methodCallsQueue)
{
switch (expression.NodeType)
{
case ExpressionType.MemberAccess:
VisitMemberExpression((MemberExpression) expression, methodCallsQueue);
break;
case ExpressionType.Call:
VisitMethodCallExpression((MethodCallExpression) expression, methodCallsQueue);
break;
}
}
private static void VisitMemberExpression(MemberExpression expression,
Queue<MethodCallExpression> methodCallsQueue)
{
var call = GenerateSafeDereferenceCall(expression.Expression.Type,
expression.Type,
expression.Expression,
p => Expression.PropertyOrField(p, expression.Member.Name));
methodCallsQueue.Enqueue(call);
VisitExpression(expression.Expression, methodCallsQueue);
}
private static void VisitMethodCallExpression(MethodCallExpression expression,
Queue<MethodCallExpression> methodCallsQueue)
{
var call = GenerateSafeDereferenceCall(expression.Object.Type,
expression.Type,
expression.Object,
p => Expression.Call(p, expression.Method, expression.Arguments));
methodCallsQueue.Enqueue(call);
VisitExpression(expression.Object, methodCallsQueue);
}
}
You can use it this way:
var street = person.SafeDereference(d=>d.addressdetails.Street);
street = person.SafeDereference(d=>d.addressdetails[1].Street);
street = person.SafeDereference(d=>d.addressdetails.FirstOrDefault().Street);
var name = person.SafeDereference(d=>d.InvoiceList.FirstOrDefault().Product.Name);
Warning : this is not fully tested, it should work with methods and properties, but probably not with extension methods inside the expression.
Edit : Ok, it can't handle extension methods for now (e.g. FirstOrDefault) but it's still possible to adjust the solution.
You would have to take the expression apart and evaluate each bit in turn, stopping when you got a null result. This wouldn't be impossible by any means, but it would be quite a lot of work.
Are you sure this is less work than just putting explicit null guards in the code?
We definately need a null-safe dereferencing operator in C#, but until then look at this question, which provides a slightly different but also neat solution to the same problem.
Any reason why you couldn't just do the following?
bool result;
result = addressdetails != null && addressdetails.Street != null;
result = addressdetails != null && addressdetails.Count > 1 && addressdetails[1].Street != null;
result = addressdetails != null && addressdetails.FirstOrDefault() != null && addressdetails.FirstOrDefault().Street != null;
result = addressdetails != null && addressdetails.FirstOrDefault() != null && addressdetails.FirstOrDefault().Product != null && addressdetails.FirstOrDefault().Product.Name != null;
I think these simple boolean expressions would be the best way to go, they work because of conditional evaluation... When anding (&&) terms together, the first false term will return false and stop the rest from being evaluated, so you wouldn't get any exceptions doing this.
Though it's not the answer on this exact question, the easiest way I know to achieve the same behaviour is to pass pathes to properties as enumerables of property names instead of call chains.
public static bool HasNull<T, Y>(this T someType, IEnumerable<string> propertyNames)
Then parse those enumerables in a circle or recursively using reflection. There are some disadvantages like loosing intelly sense and static name checking, but very easy to implement, which may overweigh them in some cases.
Instead of writing
person.HasNull(d=>d.addressdetails.FirstOrDefault().Street)
you'll have to write
person.HasNull(new string[] { "addressdetails", "0", "Street" })
I blogged about, it works in VB.NET (I translated to C#, but I didn't test the C# version, check it out).
Checkout: How would you check car.Finish.Style.Year.Model.Vendor.Contacts.FirstOrDefault().FullName for null? :-)
Dim x = person.TryGetValue(
Function(p) p.addressdetail(1).FirstOrDefault().Product.Name)

Categories

Resources