Get RuntimeMethodInfo from generic overloaded methods in non-generic static class - c#

I try to get runtime method infoes in static class. I have four static method inside the class and each name is equal, also parameter name is equal. The only difference is their types. One of four method has string parameter so it is easy to get method info. However the others don't working. I find several advice but that are not working.
All test code is here.
class Program {
static void Main(string[] args) {
//ok
var stringMethodInfo = typeof(TestClass).GetRuntimeMethod("TestMethod", new[] { typeof(string) });
//not working
var dictMethodInfo = typeof(TestClass).GetRuntimeMethod("TestMethod", new[] { typeof(Dictionary<,>) });
//not working
var genericMethodInfo = typeof(TestClass).GetRuntimeMethod("TestMethod", new[] { typeof(object) });
//not working
var listMethodInfo = typeof(TestClass).GetRuntimeMethod("TestMethod", new[] { typeof(List<>) });
//not working
var res = typeof(TestClass)
.GetRuntimeMethods()
.Where(x => x.Name.Equals("TestMethod"))
.Select(m => new { Method = m, Parameters = m.GetParameters() })
.FirstOrDefault(p =>
p.Parameters.Length == 1
&& p.Parameters[0].ParameterType.IsGenericType
&& p.Parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(ICollection<>)
);
}
}
public static class TestClass {
public static bool TestMethod(string item) {
return true;
}
public static bool TestMethod<TKey, TValue>(Dictionary<TKey, TValue> item) {
return true;
}
public static bool TestMethod<T>(T item) {
return true;
}
public static bool TestMethod<T>(List<T> item) {
return true;
}
}

If you are using .net core 2.1 or greater, you can use Type.MakeGenericMethodParameter to let you refer to a generic parameter of a method. You can use that to create a generic type argument that will work with GetMethod (not available for GetRuntimeMethod).
var stringMethodInfo = typeof(TestClass).GetRuntimeMethod("TestMethod", new[] { typeof(string) });
Type[] dictionaryTypeParameters = { typeof(Dictionary<,>).MakeGenericType(Type.MakeGenericMethodParameter(0), Type.MakeGenericMethodParameter(1)) };
MethodInfo dictMethodInfo = typeof(TestClass).GetMethod("TestMethod", 2, dictionaryTypeParameters);
MethodInfo listMethodInfo = typeof(TestClass).GetMethod("TestMethod", 1, new[] { typeof(List<>).MakeGenericType(Type.MakeGenericMethodParameter(0)) });
MethodInfo genericMethodInfo = typeof(TestClass).GetMethod("TestMethod", 1, new[] { Type.MakeGenericMethodParameter(0) });
Some interesting reading on the topic here.

Let's say we would like to use this method to get any MethodInfo for the various TestMethod. Note that they all have exactly one parameter, so p.Parameters.Length == 1 is useless:
Defined as bool TestMethod(string item). We can use
.FirstOrDefault(p => p.Method.IsGenericMethod)
Defined as bool TestMethod<TKey, TValue>(Dictionary<TKey, TValue> item)
.FirstOrDefault(p =>
p.Method.IsGenericMethod &&
p.Method.GetGenericArguments().Length == 2)
Defined as bool TestMethod<T>(T item)
.FirstOrDefault(p =>
p.Method.IsGenericMethod &&
p.Parameters[0].ParameterType == m.Method.GetGenericArguments()[0]
)
Defined as TestMethod<T>(List<T> item)
.FirstOrDefault(p =>
p.Method.IsGenericMethod &&
p.Parameters[0].ParameterType.GetGenericTypeDefinition() == typeof(List<>)
)

In case of generic methods you have to query the MethodInfo object to get the appropriate method.
You can do it as below -
var dictMethodInfo = typeof(TestClass).GetMethods().Single(m => m.Name == "TestMethod" && m.IsGenericMethod &&
m.GetGenericMethodDefinition().GetParameters()[0].ParameterType.IsGenericType &&
m.GetGenericMethodDefinition().GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Dictionary<,>));
In your case, getting the MethodInfo for TestMethod<T> is bit tricky, but below should work -
var genericMethodInfo = typeof(TestClass).GetMethods().Single(m => m.Name == "TestMethod" && m.IsGenericMethod &&
!m.GetGenericMethodDefinition().GetParameters()[0].ParameterType.IsGenericType);
Final Code -
var stringMethodInfo = typeof(TestClass).GetRuntimeMethod("TestMethod", new[] { typeof(string) });
var dictMethodInfo = typeof(TestClass).GetMethods().Single(m => m.Name == "TestMethod" && m.IsGenericMethod &&
m.GetGenericMethodDefinition().GetParameters()[0].ParameterType.IsGenericType &&
m.GetGenericMethodDefinition().GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Dictionary<,>));
var genericMethodInfo = typeof(TestClass).GetMethods().Single(m => m.Name == "TestMethod" && m.IsGenericMethod &&
!m.GetGenericMethodDefinition().GetParameters()[0].ParameterType.IsGenericType);
var listMethodInfo = typeof(TestClass).GetMethods().Single(m => m.Name == "TestMethod" && m.IsGenericMethod &&
m.GetGenericMethodDefinition().GetParameters()[0].ParameterType.IsGenericType &&
m.GetGenericMethodDefinition().GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(List<>));

Related

Entity Framework Core use Dbset without having T

I want to be able use a type instead of T with Dbset
For example Dbset(myType) instead of DbSet
EF core 3.1 requires you to use DbSet and all the functionality is encapsulated in Extension methods.
Here's a solution I went with for EF 3.1
Hoping that since I've already been through the pain, I can share my solution for the next poor soul who has to do this.
Usage
var dbSet = new DbSetFacade(myType);
dynamic results = await dbSet.ToListAsync();
I'll just share ToList,Count for now to keep it brief!
public class DbSetFacade
{
private static ReadOnlyDictionary<EfQueryExtensionType, MethodInfo> _extensionMethodInfos = GetExtensionMethodInfos();
private readonly object _dbSet;
private readonly ReadOnlyDictionary<EfQueryExtensionType, MethodInfo> _genericInstanceMethodInfos;
public DbSetFacade(DbContext context, Type entityType)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (entityType == null) throw new ArgumentNullException(nameof(entityType));
_dbSet = GetDbSetInstance(context, entityType);
_genericInstanceMethodInfos = new ReadOnlyDictionary<EfQueryExtensionType, MethodInfo>(new Dictionary<EfQueryExtensionType, MethodInfo>
{
[EfQueryExtensionType.AsNoTracking] = _extensionMethodInfos[EfQueryExtensionType.AsNoTracking].MakeGenericMethod(entityType),
[EfQueryExtensionType.ToListAsync] = _extensionMethodInfos[EfQueryExtensionType.ToListAsync].MakeGenericMethod(entityType),
[EfQueryExtensionType.CountAsyncWithoutPredicate] = _extensionMethodInfos[EfQueryExtensionType.CountAsyncWithoutPredicate].MakeGenericMethod(entityType),
});
}
enum EfQueryExtensionType
{
AsNoTracking,
ToListAsync,
//FirstOrDefaultAsyncWithPredicate,
//FirstOrDefaultAsyncWithoutPredicate,
//ContainsAsync,
//CountAsyncWithPredicate,
CountAsyncWithoutPredicate
}
public Task<int> CountAsync(CancellationToken token = default) => _genericInstanceMethodInfos[EfQueryExtensionType.CountAsyncWithoutPredicate].Invoke(null, new object[] { _dbSet, token }) as Task<int>;
public async Task<dynamic> ToListAsync(CancellationToken token = default)
{
var noTrackingSet = _genericInstanceMethodInfos[EfQueryExtensionType.AsNoTracking].Invoke(null, new object[] { _dbSet });
var toList = _genericInstanceMethodInfos[EfQueryExtensionType.ToListAsync].Invoke(null, new object[] { noTrackingSet, token }) as Task;
await toList.ConfigureAwait(false);
return (object)((dynamic)toList).Result;
}
private static object GetDbSetInstance(DbContext context, Type type)
{
return typeof(DbContext).GetMethods()
.FirstOrDefault(p => p.Name == "Set" && p.ContainsGenericParameters)
.MakeGenericMethod(type)
.Invoke(context, null);
}
static ReadOnlyDictionary<EfQueryExtensionType, MethodInfo> GetExtensionMethodInfos()
{
var extensionMethods = typeof(EntityFrameworkQueryableExtensions)
.GetTypeInfo()
.GetMethods()
.Where(x => x.ContainsGenericParameters && x.GetParameters()[0].ParameterType.IsGenericType);
return new ReadOnlyDictionary<EfQueryExtensionType, MethodInfo>(new Dictionary<EfQueryExtensionType, MethodInfo>
{
[EfQueryExtensionType.AsNoTracking] = extensionMethods.FirstOrDefault(x => x.Name == "AsNoTracking"),
[EfQueryExtensionType.ToListAsync] = extensionMethods.FirstOrDefault(x => x.Name == "ToListAsync" && x.GetParameters().Count() == 2),
[EfQueryExtensionType.CountAsyncWithoutPredicate] = extensionMethods.FirstOrDefault(x => x.Name == "CountAsync" && x.GetParameters().Count() == 2)
});
}
}

Call a local generic method by reflection

I need to call a local generic method by reflection, but I can't figure out how to do it.
My core problem is that I'm using reflection to populate an POCO object from a set of queries reading EAV data from a MySQL database. I'm happy to show this code, but it's lengthy and complex. I've created a simplified example that I think summarizes the problem nicely.
Consider this code:
void Main()
{
var repository = new Repository();
repository.Store<Foo>(xs => xs.Count());
int Compute<M>(string[] source) => repository.Fetch<M>().Invoke(source);
Console.WriteLine(Compute<Foo>(new[] { "A", "B" }));
}
public class Foo { }
public class Repository
{
private Dictionary<Type, object> _store = new Dictionary<Type, object>();
public void Store<T>(Func<string[], int> value)
{
_store[typeof(T)] = value;
}
public Func<string[], int> Fetch<T>()
{
return (Func<string[], int>)_store[typeof(T)];
}
}
I have a Repository type that can store a Func<string[], int> indexed by Type.
In my Main method I have instantiated an instance of Repository and stored xs => xs.Count() against the type Foo.
I have a local generic method Compute that I can call easily when I know the type of M at compile time. The call Compute<Foo>(new[] { "A", "B" }) computes 2. That's no problem.
The issue occurs when I only know M at run-time.
Let's say I have this type:
public class Bar
{
public Foo Value;
}
Now my code in Main looks like this:
void Main()
{
var repository = new Repository();
repository.Store<Foo>(xs => xs.Count());
int Compute<M>(string[] source) => repository.Fetch<M>().Invoke(source);
var runtimeType = typeof(Bar).GetField("Value").FieldType;
var reflectedMethod = /* What Goes Here ??? */
Console.WriteLine(reflectedMethod.Invoke(new[] { "A", "B" }));
}
I am thoroughly stumped with the /* What Goes Here ??? */ part.
I found a similar question but that method doesn't deal with a local generic method.
Can someone help with how to invoke such a method at run-time?
Here's the full working code. It runs in LINQPad with NuGet "System.Interactive" and a also Maybe monad library, with a reference to my MySQL database.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
internal class MagentoAttribute : Attribute
{
public string AttributeCode { get; private set; }
public MagentoAttribute(string code)
{
this.AttributeCode = code;
}
}
public static class Magento
{
public abstract class ProductBase
{
public uint EntityId { get; private set; }
public string Sku { get; private set; }
public decimal? Quantity { get; private set; }
}
public static T[] GetProducts<T>(this UserQuery #this) where T : ProductBase, new()
{
var setEntityId = typeof(ProductBase).GetProperty("EntityId", BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.NonPublic | BindingFlags.Public);
var setSku = typeof(ProductBase).GetProperty("Sku", BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.NonPublic | BindingFlags.Public);
var setQuantity = typeof(ProductBase).GetProperty("Quantity", BindingFlags.Instance | BindingFlags.SetProperty | BindingFlags.NonPublic | BindingFlags.Public);
var results =
(
from cpe in #this.catalog_product_entity
where cpe.sku != null && !cpe.sku.StartsWith("SHIP")
join csi in #this.cataloginventory_stock_item on cpe.entity_id equals csi.product_id
select new { cpe.entity_id, cpe.sku, csi.qty }
)
.ToArray()
.Select(x =>
{
var t = new T();
setEntityId.SetValue(t, x.entity_id);
setSku.SetValue(t, x.sku);
setQuantity.SetValue(t, x.qty);
return t;
})
.ToArray();
Func<string[], Dictionary<string, Func<uint, Maybe<string>>>> getStrings = attributeCodes =>
(
from eet in #this.eav_entity_type
where eet.entity_type_code == "catalog_product"
from ea in #this.eav_attribute
where ea.entity_type_id == eet.entity_type_id
where attributeCodes.Contains(ea.attribute_code)
join cpev in #this.catalog_product_entity_varchar on ea.attribute_id equals cpev.attribute_id
where cpev.store_id == 0
select new { ea.attribute_code, cpev.entity_id, cpev.value }
)
.Concat(
from eet in #this.eav_entity_type
where eet.entity_type_code == "catalog_product"
from ea in #this.eav_attribute
where ea.entity_type_id == eet.entity_type_id
where attributeCodes.Contains(ea.attribute_code)
join cpev in #this.catalog_product_entity_text on ea.attribute_id equals cpev.attribute_id
where cpev.store_id == 0
select new { ea.attribute_code, cpev.entity_id, cpev.value }
)
.ToArray()
.GroupBy(x => x.attribute_code, x => new { x.entity_id, x.value })
.Select(x => new
{
attribute_code = x.Key,
f = x.ToDictionary(y => y.entity_id, y => y.value.ToMaybe()).Map(k => Maybe<string>.Nothing)
})
.ToDictionary(x => x.attribute_code, x => x.f);
Func<string[], Dictionary<string, Func<uint, Maybe<int?>>>> getIntegers = attributeCodes =>
(
from eet in #this.eav_entity_type
where eet.entity_type_code == "catalog_product"
from ea in #this.eav_attribute
where ea.entity_type_id == eet.entity_type_id
where attributeCodes.Contains(ea.attribute_code)
join cpev in #this.catalog_product_entity_int on ea.attribute_id equals cpev.attribute_id
where cpev.store_id == 0
select new { ea.attribute_code, cpev.entity_id, cpev.value }
)
.ToArray()
.GroupBy(x => x.attribute_code, x => new { x.entity_id, x.value })
.Select(x => new
{
attribute_code = x.Key,
f = x.ToDictionary(y => y.entity_id, y => y.value.ToMaybe()).Map(k => Maybe<int?>.Nothing)
})
.ToDictionary(x => x.attribute_code, x => x.f);
Func<string[], Dictionary<string, Func<uint, Maybe<DateTime?>>>> getDateTimes = attributeCodes =>
(
from eet in #this.eav_entity_type
where eet.entity_type_code == "catalog_product"
from ea in #this.eav_attribute
where ea.entity_type_id == eet.entity_type_id
where attributeCodes.Contains(ea.attribute_code)
join cpev in #this.catalog_product_entity_datetime on ea.attribute_id equals cpev.attribute_id
where cpev.store_id == 0
select new { ea.attribute_code, cpev.entity_id, cpev.value }
)
.ToArray()
.GroupBy(x => x.attribute_code, x => new { x.entity_id, x.value })
.Select(x => new
{
attribute_code = x.Key,
f = x.ToDictionary(y => y.entity_id, y => y.value.ToMaybe()).Map(k => Maybe<DateTime?>.Nothing)
})
.ToDictionary(x => x.attribute_code, x => x.f);
Func<string[], Dictionary<string, Func<uint, Maybe<decimal?>>>> getDecimals = attributeCodes =>
(
from eet in #this.eav_entity_type
where eet.entity_type_code == "catalog_product"
from ea in #this.eav_attribute
where ea.entity_type_id == eet.entity_type_id
where attributeCodes.Contains(ea.attribute_code)
join cpev in #this.catalog_product_entity_decimal on ea.attribute_id equals cpev.attribute_id
where cpev.store_id == 0
select new { ea.attribute_code, cpev.entity_id, cpev.value }
)
.ToArray()
.GroupBy(x => x.attribute_code, x => new { x.entity_id, x.value })
.Select(x => new
{
attribute_code = x.Key,
f = x.ToDictionary(y => y.entity_id, y => y.value.ToMaybe()).Map(k => Maybe<decimal?>.Nothing)
})
.ToDictionary(x => x.attribute_code, x => x.f);
var prerepo = new Prerepository();
prerepo.Store<Maybe<string>>(getStrings);
prerepo.Store<Maybe<int?>>(getIntegers);
prerepo.Store<Maybe<DateTime?>>(getDateTimes);
prerepo.Store<Maybe<decimal?>>(getDecimals);
var collapse = new Dictionary<Type, Delegate>()
{
{ typeof(Maybe<int?>), (Func<Maybe<int?>, Maybe<int?>>)(m => m) },
{ typeof(Maybe<int>), (Func<Maybe<int?>, Maybe<int>>)(m => (!m.HasValue || (m.HasValue && m.Value == null)) ? Maybe<int>.Nothing : m.Value.Value.ToMaybe()) },
{ typeof(int?), (Func<Maybe<int?>, int?>)(m => m.HasValue ? m.Value : (int?)null) },
{ typeof(int), (Func<Maybe<int?>, int>)(m => (m.HasValue && m.Value != null) ? m.Value.Value : default(int)) },
{ typeof(Maybe<decimal?>), (Func<Maybe<decimal?>, Maybe<decimal?>>)(m => m) },
{ typeof(Maybe<decimal>), (Func<Maybe<decimal?>, Maybe<decimal>>)(m => (!m.HasValue || (m.HasValue && m.Value == null)) ? Maybe<decimal>.Nothing : m.Value.Value.ToMaybe()) },
{ typeof(decimal?), (Func<Maybe<decimal?>, decimal?>)(m => m.HasValue ? m.Value : (decimal?)null) },
{ typeof(decimal), (Func<Maybe<decimal?>, decimal>)(m => (m.HasValue && m.Value != null) ? m.Value.Value : default(decimal)) },
{ typeof(Maybe<DateTime?>), (Func<Maybe<DateTime?>, Maybe<DateTime?>>)(m => m) },
{ typeof(Maybe<DateTime>), (Func<Maybe<DateTime?>, Maybe<DateTime>>)(m => (!m.HasValue || (m.HasValue && m.Value == null)) ? Maybe<DateTime>.Nothing : m.Value.Value.ToMaybe()) },
{ typeof(DateTime?), (Func<Maybe<DateTime?>, DateTime?>)(m => m.HasValue ? m.Value : (DateTime?)null) },
{ typeof(Maybe<string>), (Func<Maybe<string>, Maybe<string>>)(m => m) },
{ typeof(string), (Func<Maybe<string>, string>)(m => m.HasValue ? m.Value : default(string)) },
};
var attributes =
Enumerable
.Concat(
typeof(T)
.GetFields(BindingFlags.GetField | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(x => new
{
Field = x,
Attribute = x.GetCustomAttribute<MagentoAttribute>(),
})
.Where(x => x.Attribute != null)
.Select(x => new
{
Inject = (Action<object, object>)((o, v) => x.Field.SetValue(o, v)),
x.Attribute.AttributeCode,
AttributeType = x.Field.FieldType
}),
typeof(T)
.GetProperties(BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Select(x => new
{
Property = x,
Attribute = x.GetCustomAttribute<MagentoAttribute>(),
})
.Where(x => x.Attribute != null)
.Select(x => new
{
Inject = (Action<object, object>)((o, v) => x.Property.SetValue(o, v)),
x.Attribute.AttributeCode,
AttributeType = x.Property.PropertyType
}))
.Where(x => collapse.ContainsKey(x.AttributeType))
.ToArray();
var postrepo = new Postrepository();
postrepo.Store<Maybe<int?>>(null);
void Fetch<M>(string[] attributeCodes) => postrepo.Store<M>(prerepo.Fetch<M>()(attributeCodes));
void InvokeHelper(Action<string[]> prototype, Type type, object data)
{
var method = prototype.Method;
var genericMethod = method.GetGenericMethodDefinition();
var concreteMethod = genericMethod.MakeGenericMethod(type);
concreteMethod.Invoke(prototype.Target, new[] { data });
}
Type lift(Type t)
{
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Maybe<>))
{
t = t.GetGenericArguments()[0];
}
if (!t.IsGenericType)
{
if (t.IsValueType)
{
t = typeof(Nullable<>).MakeGenericType(t);
}
}
t = typeof(Maybe<>).MakeGenericType(t);
return t;
}
(
from a in attributes
let m = lift(a.AttributeType)
group a.AttributeCode by m
)
.ForEach(x => InvokeHelper(Fetch<object>, x.Key, x.Distinct().ToArray()));
void Inject<M, P>(string attributeCode, Action<object, object> inject)
{
var b = postrepo.Fetch<M>();
var d = b[attributeCode];
var fmp = (Func<M, P>)collapse[typeof(P)];
results
.ForEach(r =>
{
var m = d(r.EntityId);
inject(r, fmp(m));
});
}
void InvokeHelper2(Action<string, Action<object, object>> prototype, Type[] types, object[] data)
{
var method = prototype.Method;
var genericMethod = method.GetGenericMethodDefinition();
var concreteMethod = genericMethod.MakeGenericMethod(types);
concreteMethod.Invoke(prototype.Target, data);
}
(
from a in attributes
let m = lift(a.AttributeType)
group new { a.AttributeType, a.Inject } by new { a.AttributeCode, m }
)
.ForEach(xs =>
{
xs
.ForEach(y =>
{
InvokeHelper2(Inject<object, object>, new[] { xs.Key.m, y.AttributeType }, new object[] { xs.Key.AttributeCode, y.Inject });
});
});
return results;
}
public class Prerepository
{
private Dictionary<Type, object> _store = new Dictionary<Type, object>();
public void Store<T>(Func<string[], Dictionary<string, Func<uint, T>>> value)
{
_store[typeof(T)] = value;
}
public Func<string[], Dictionary<string, Func<uint, T>>> Fetch<T>()
{
return (Func<string[], Dictionary<string, Func<uint, T>>>)_store[typeof(T)];
}
}
public class Postrepository
{
private Dictionary<Type, object> _store = new Dictionary<Type, object>();
public void Store<T>(Dictionary<string, Func<uint, T>> value)
{
_store[typeof(T)] = value;
}
public Dictionary<string, Func<uint, T>> Fetch<T>()
{
return (Dictionary<string, Func<uint, T>>)_store[typeof(T)];
}
}
}
Now I can write this code:
public class Product : Magento.ProductBase
{
[Magento("name")] public string Name { get; private set; }
[Magento("is_deleted")] private Maybe<int?> _is_deleted = Maybe<int?>.Nothing;
public bool IsDeleted { get => _is_deleted.HasValue ? _is_deleted.Value == 1 : false; }
[Magento("brand")] private Maybe<string> _brand { get; set; } = Maybe<string>.Nothing;
public string Brand { get => _brand.HasValue ? _brand.Value : "(missing)"; }
[Magento("cost")] public decimal Cost { get; private set; }
}
And then this:
var ps =
Magento
.GetProducts<Product>(this)
.Where(x => x.Cost == 0m)
.Where(x => !x.IsDeleted)
.Where(x => x.Quantity > 0m);
And I now have a strongly-typed way to read the Magento EAV data structure.
Jon Skeets answer will work for your problem too.
I only changed the signature to match your requirements:
var repository = new Repository();
repository.Store<Foo>(xs => xs.Count());
int Compute<M>(string[] source) => repository.Fetch<M>().Invoke(source);
// Call Compute<M>
var runtimeKnownTime = typeof(Foo);
var computeResult = InvokeHelper(Compute<object>, new[] { "A", "B" }, runtimeKnownTime);
Console.WriteLine(computeResult);
This uses the following implementation of InvokeHelper:
// This code was created by Jon Skeet : https://stackoverflow.com/a/43349127/2729609
// I only changed Action<int> to Func<int, int> and changed the return type.
private static int InvokeHelper(Func<string[], int> int32Func, object data, Type type)
{
// You probably want to validate that it really is a generic method...
var method = int32Func.Method;
var genericMethod = method.GetGenericMethodDefinition();
var concreteMethod = genericMethod.MakeGenericMethod(type);
return (int)concreteMethod.Invoke(int32Func.Target, new[] { data });
}
DEMO
BUT I don't see the need to use this hack. You don't need the generic type in your case.
Change the repository to:
public class Repository
{
private Dictionary<Type, object> _store = new Dictionary<Type, object>();
public void Store(Type id, Func<string[], int> value)
{
_store[id] = value;
}
// optional
public void Store<T>(Func<string[], int> value) => this.Store(typeof(T), value);
public Func<string[], int> Fetch(Type id)
{
return (Func<string[], int>)_store[id];
}
// optional
public Func<string[], int> Fetch<T>() => this.Fetch(typeof(T));
}
And you can use it without generics:
var repository = new Repository();
repository.Store(typeof(Foo), xs => xs.Count());
int Compute(Type id, string[] source) => repository.Fetch(id).Invoke(source);
// Call Compute<M>
var runtimeKnownTime = typeof(Foo);
Console.WriteLine(Compute(runtimeKnownTime, new[] { "A", "B" }));
If you want, create generic overloads for the methods Fetch and Store that call the shown implementation using typeof(T). I markted this method with optional in my sample implemenation.

C# Reflection - Choose Which Overload of a Particular Method to run Based on Number of Parameters [duplicate]

I have this type that contains two overloads of a generic method. I like to retrieve one of the overloads (with the Func<T> parameter) using reflection. The problem however is that I can't find the correct parameter type to supply the Type.GetMethod(string, Type[]) method with.
Here is my class definition:
public class Foo
{
public void Bar<T>(Func<T> f) { }
public void Bar<T>(Action<T> a) { }
}
And this is what I've come up with, unfortunately without succes:
[TestMethod]
public void Test1()
{
Type parameterType = typeof(Func<>);
var method = typeof(Foo).GetMethod("Bar", new Type[] { parameterType });
Assert.IsNotNull(method); // Fails
}
How can I get the MethodInfo of a generic method of which I know the parameters?
Why don't you use expression trees? This makes it much easier:
public static MethodInfo GetMethod<T>(
Expression<Action<T>> methodSelector)
{
var body = (MethodCallExpression)methodSelector.Body;
return body.Method;
}
[TestMethod]
public void Test1()
{
var expectedMethod = typeof(Foo)
.GetMethod("Bar", new Type[] { typeof(Func<>) });
var actualMethod =
GetMethod<Foo>(foo => foo.Bar<object>((Func<object>)null)
.GetGenericMethodDefinition();
Assert.AreEqual(expectedMethod, actualMethod);
}
Surprisingly, it looks like you'll need to call GetMethods() and loop over the methods until you find the one you want.
For example:
var yourMethod = typeof(Foo).GetMethods()
.First(m => m.Name == "Bar"
&& m.GetParameters().Length == 1
&& m.GetParameters()[0].ParameterType.ContainsGenericParameters
&& m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(Func<>));
I don't think you can do this directly using GetMethod. I suspect you'll have to iterate over all the methods called Bar, then:
Check that the method has one type parameter
Check that the method has one normal parameter
Use the type parameter to make a Func<T> (with typeof(Func<>).MakeGenericType) and check that the parameter type matches that.
LINQ is good for this sort of thing. Complete sample:
using System;
using System.Reflection;
using System.Linq;
public class Foo
{
public void Bar<T>(Func<T> f) { }
public void Bar<T>(Action<T> a) { }
}
class Test
{
static void Main()
{
var methods = from method in typeof(Foo).GetMethods()
where method.Name == "Bar"
let typeArgs = method.GetGenericArguments()
where typeArgs.Length == 1
let parameters = method.GetParameters()
where parameters.Length == 1
where parameters[0].ParameterType ==
typeof(Func<>).MakeGenericType(typeArgs[0])
select method;
Console.WriteLine("Matching methods...");
foreach (var method in methods)
{
Console.WriteLine(method);
}
}
}
Basically generics and reflection are really nasty in combination, I'm afraid :(
You need to specify a concrete type using MethodInfo.MakeGenericMethod.
However, I should point out, that getting the right type to invoke MakeGenericMethod on is not easy when you have an overloaded generic method.
Here is an example:
var method = typeof(Foo)
.GetMethods()
.Where(x => x.Name == "Bar")
.Where(x => x.IsGenericMethod)
.Where(x => x.GetGenericArguments().Length == 1)
.Where(x => x.GetParameters().Length == 1)
.Where(x =>
x.GetParameters()[0].ParameterType ==
typeof(Action<>).MakeGenericType(x.GetGenericArguments()[0])
)
.Single();
method = method.MakeGenericMethod(new Type[] { typeof(int) });
Foo foo = new Foo();
method.Invoke(foo, new Func<int>[] { () => return 42; });
You'll struggle with just with just GetMethod - you could try something along the lines of;
var method = (from m in typeof(Foo).GetMethods()
where
m.IsGenericMethodDefinition == true &&
m.Name == "Bar" &&
m.GetParameters().Length > 0 &&
m.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == parameterType
select m).FirstOrDefault();

Get all class that implement class with specific generic calsses

I would like to find all classes in my program that implements a given class with a specific call as the generic types.
In the example below would i like to find all classes that implements MyBaseClass<MyScraper, MyElance> in this case would it be MyProperty and not OtherProperty as it implements other generic classes.
How can this be done?
public class MyProperty : MyBaseClass<MyScraper, MyElance>
{
public override string test()
{
var test = base.test();
test += " + the new";
return test;
}
}
public class OtherProperty : MyBaseClass<OtherScraper, OtherElance>
{
public override string test()
{
var test = base.test();
test += " + the other";
return test;
}
}
public class MyBaseClass<S, E>
where S : IScraper
where E : IElance
{
public virtual string test()
{
return "base";
}
}
Edit:
Found a solution, but please tell me if there are a better way
var test = from x in Assembly.GetAssembly(typeof(Program)).GetTypes()
let y = x.BaseType
where !x.IsAbstract && !x.IsInterface &&
y != null && y.IsGenericType &&
y.GetGenericTypeDefinition() == typeof(MyBaseClass<,>) &&
y.GenericTypeArguments[0] == typeof(MyScraper) &&
y.GenericTypeArguments[1] == typeof(MyElance)
select x;
Final solution:
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(x => !x.IsAbstract && !x.IsInterface)
.Where(x => x.BaseType != null &&
x.BaseType.IsGenericType &&
x.BaseType.GetGenericTypeDefinition() == typeof (MyBaseClass<,>) &&
x.BaseType.GenericTypeArguments[0] == typeof (MyScraper) &&
x.BaseType.GenericTypeArguments[1] == typeof (MyElance))
.Select(x => x).ToList();
Assembly scanning is pretty much the best way.
In addition to the algorythm you have, I recommend scanning the AppDomain instead of the specific assembly - this will allow you (or others!) toextend your library more readily...
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
//...
(Disclaimer Pardon the use of Linq calls here, but I detest syntactical linq)
In addition, make sure any other assemblies are loaded, to ensure you don't miss something at scan-time.
var searchType = typeof(MyBaseClass<MyScraper, MyElance>);
var types =
AppDomain.CurrentDomain.GetAssemblies()
// For search only on assemblies where could exsist this types
.Where(a => a.GetName().Name == searchType.Assembly.GetName().Name || a.GetReferencedAssemblies().Any(n => n.Name == searchType.Assembly.GetName().Name))
.Where(t => !t.IsAbstract && !t.IsInterface)
.Select(t => t.GetTypes().Where(a => searchType.IsAssignableFrom(a)))
.SelectMany(a => a);
I use this version, to search in all loaded assemblies, with additional filter not to process assemblies that could not contain my type
Try this:
//var desiredImplementation = typeof (MyBaseClass<>).MakeGenericType(typeof (MyScraper), typeof (MyElance));
var desiredImplementation = typeof (MyBaseClass<MyScraper, MyElance>);
var implementingTypes = Assembly
.GetExecutingAssembly()
.GetExportedTypes()
.Where(type => desiredImplementation.IsAssignableFrom(type))
.ToList();
You can replace GetExportedTypes() with GetTypes() and BindingFlags to further introspect the assembly too.

How to create a Func<> delegate programmatically

I have a small dependency injection framework, and I am trying to make it resolve Lazy<> instances dynamically. The idea is to do something like that:
DIContainer.Register<IDbCommand,SqlCommand>();
var lazyCommand = DIContainer.Resolve<Lazy<IDbCommand>>();
I read the other day that Autofac was able of doing that.
I am stuck trying to set the constructor for that Lazy<> instance. In the next test code, a exception is thrown because the desired type constructor is expecting a Func<arg>, but I am passing a Func<Object>:
static readonly Type _lazyType = typeof(Lazy<>);
static Object ResolveTest(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == _lazyType)
{
var arg = type.GetGenericArguments()[0];
return Activator.CreateInstance(_lazyType.MakeGenericType(arg), new Func<Object>(() => ResolveType(arg)));
}
else
return ResolveType(type);
}
I am out of ideas about how to create a delegate that fits for the Lazy<> constructor parameter. Any idea?
Cheers.
That's not trivial. One possible solution would be to work with reflection:
Create a generic ResolveType method:
public static T ResolveType<T>()
{
return (T)ResolveType(typeof(T));
}
Create a delegate that uses this method:
// You probably want to cache this MethodInfo:
var method = typeof(TypeContainingResolveType)
.GetMethods()
.Single(x => x.IsGenericMethod &&
x.Name == "ResolveType")
.MakeGenericMethod(arg);
var delegate = Delegate.CreateDelegate(
typeof(Func<>).MakeGenericType(arg),
method);
Use that delegate:
return Activator.CreateInstance(_lazyType.MakeGenericType(arg), delegate);
This app outputs "True" and "0". I.e. ResolveTest(typeof(Lazy<int>)) returns a Lazy<int> object, constructed like you wanted.
using System;
using System.Linq.Expressions;
namespace TestApp
{
public class Class1
{
public static void Main()
{
object lazyInt = ResolveTest(typeof(Lazy<int>));
Console.WriteLine(lazyInt.GetType() == typeof(Lazy<int>));
Console.WriteLine(((Lazy<int>)lazyInt).Value);
}
static readonly Type _lazyType = typeof(Lazy<>);
static Object ResolveTest(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == _lazyType)
{
var arg = type.GetGenericArguments()[0];
var lazyArgType = _lazyType.MakeGenericType(arg);
var funcArgType = typeof(Func<>).MakeGenericType(arg);
var funcCtor = lazyArgType.GetConstructor(new[] { funcArgType });
Expression<Func<object>> f = () => ResolveTest(arg);
var func = typeof(Class1).GetMethod("BuildCastedThing").MakeGenericMethod(arg).Invoke(null, new[] { f });
var arguments = new object[] { func };
var retVal = funcCtor.Invoke(arguments);
return retVal;
}
else
return ResolveType(type);
}
public static object ResolveType(Type type)
{
return Activator.CreateInstance(type);
}
public static Func<T> BuildCastedThing<T>(Expression<Func<object>> f)
{
Expression<Func<T>> expr =
Expression.Lambda<Func<T>>(
Expression.Convert(
Expression.Invoke(f),
typeof(T)));
return expr.Compile();
}
}
}
This is a way to rewrite ResolveTest as a generic Resolve<T> (e.g. Resolve<int> returns Lazy<int>). This is a little different, since there's no equivalent to ResolveTest(typeof(int)), which returns an int.
static Lazy<T> Resolve<T>()
{
var arg = typeof(T);
return new Lazy<T>(() => (T)ResolveType(arg));
}
Or with a generic ResolveType<T>:
static Lazy<T> Resolve<T>()
{
return new Lazy<T>(() => ResolveType<T>());
}
public static T ResolveType<T>()
{
return Activator.CreateInstance<T>();
}
public static Object ResolveTest(Type type)
{
if (type.IsGenericType && type.GetGenericTypeDefinition() == _lazyType)
{
var arg = type.GetGenericArguments()[0];
Expression<Func<object>> expressionWithFuncOfTypeObject = () => ResolveType(arg);
UnaryExpression expressionThatEvaluatesToAnObjectOfTypeArg = Expression.Convert(expressionWithFuncOfTypeObject.Body, arg);
LambdaExpression expressionWithFuncOfTypeArg = Expression.Lambda(typeof(Func<>).MakeGenericType(arg), expressionThatEvaluatesToAnObjectOfTypeArg);
Delegate funcOfTypeArg = expressionWithFuncOfTypeArg.Compile(); // <-- At runtime this will be of type Func<T>
return Activator.CreateInstance(_lazyType.MakeGenericType(arg), funcOfTypeArg);
}
else
return ResolveType(type);
}

Categories

Resources