I think I have a lack of understand, what exactly is happening:
The user can enter a Path to an Assembly and an Object type and I try to create a instance of it.
My approach:
Assembly a = Assembly.LoadFile(txtAssemblyPath.Text);
Type myType = a.GetTypes().ToList().FirstOrDefault(f => f.Name == txtObjectName.Text);
var obj = Activator.CreateInstance<myType>();
var obj2 =(myType) Activator.CreateInstance(myType);
The problem is in the Creation of the Object itself. It seems like the myType is not threated as a Type. On this example: Creating generic variables from a type - How? Or use Activator.CreateInstance() with properties { } instead of parameters ( )?
They just get an object, so I guess this is not the same Case. What I dont understand at all: CreateInstance(Type) works, but CreateInstance with the Type doesn't, but T and Type should be the same: System.Type.
Thanks in advance for clarifying.
Matthias
There are a using difference... When you write:
var obj = Activator.CreateInstance<myType>();
You used your Class like a Type, and it's the good way to do it.
You used a generic type who have a reference to a class type.
But there:
var obj2 =(myType) Activator.CreateInstance(myType);
You used you class like an instance (object). And you can't do that, a class is a schema.
If you want to call the second method, you have to write:
var obj2 =(myType) Activator.CreateInstance(typeof(myType));
This code will create an instance of the class Type, and this instance will describe your class myType.
I hope to be clear.
A class is a schema, you can create an object with this schema, it will be an instance (a memory-object of your class).
When you use generic type of method such as Activator.CreateInstance<T>(); you have to provide Strong Type of T. That means you have to pass known type name instead of T. for example:
var activatorPerson = (Person)Activator.CreateInstance<Person>();
That's why there is a non generic form of Activator.CreateInstance(typeGoesHere) function which can be used in cases that we do not have a Strong Type at moment of creation of an object. So we can pass type as a parameter to that function. we can provide type variable in lots of ways. In your case you find the proper type in your assembly as following:
Type myType = a.GetTypes().ToList().FirstOrDefault(f => f.Name == txtObjectName.Text);
also you have to notice that explicit casting as you typed in your code is not valid:
obj2 =(myType) Activator.CreateInstance(myType);
because you have to provide Strong Type name for explicit casting.
when we do not have access to strong type names at run time, we have to use non generic versions of methods.
Here is the pure dynamic way.
This is the factory class and dynamic instance create method:
public class RepositoryFactory
{
public static dynamic CreateDynamic<TEntity>() where TEntity : BaseEntity
{
dynamic repositoryInstance = null;
var subRepositories = AssemblyHelper.GetSubclassesOf(typeof(BaseRepository<TEntity>), true);
var entityTypeName = typeof(TEntity).Name;
var subRepository = subRepositories.FirstOrDefault(x => x.Name == entityTypeName + "Repository");
if (subRepository != null)
{
var repositoryType = subRepository.UnderlyingSystemType;
repositoryInstance = Activator.CreateInstance(repositoryType);
}
return repositoryInstance;
}
}
This is the helper class for mapping types between Entity and Repository.
public static class AssemblyHelper
{
public static List<Type> GetSubclassesOf(Type type, bool ignoreSystem)
{
List<Type> lReturn = new List<Type>();
foreach (var a in System.Threading.Thread.GetDomain().GetAssemblies())
{
if (ignoreSystem && a.FullName.StartsWith("System."))
{
continue;
}
foreach (var t in a.GetTypes())
{
if (t.IsSubclassOf(type) || (type.IsInterface && t.GetInterfaces().FirstOrDefault(e => e.FullName == type.FullName) != null))
{
lReturn.Add(t);
}
}
}
return lReturn;
}
}
This is the Manager class for use case:
public class PageManager
{
private readonly ContentPageRepository _pageRepository;
public PageManager()
{
_pageRepository = RepositoryFactory.CreateDynamic<ContentPage>();
}
public void GetPagesByContentType(ContentType type)
{
var pages = _pageRepository.GetByPredicate(x => x.Status != EntityStatus.Deleted && x.Node.Type == type);
foreach (var page in pages)
{
//Deal with it :)
}
}
}
Related
I am working with the Dapper.FluentMap library, and trying to set up auto-registration of my mapping classes. To do that, I need to call FluentMapConfiguration.AddMap<T>(EntityBase<T>) for each of the mapping classes.
I can do it like this:
public class TypeAMap : EntityMap<TypeA> {}
public class TypeBMap : EntityMap<TypeB> {}
public class TypeCMap : EntityMap<TypeC> {}
public void Register(FluentMapConfiguration configuration)
{
configuration.AddMap(new TypeAMap());
configuration.AddMap(new TypeBMap());
configuration.AddMap(new TypeCMap());
// I have a hundred of these, you can see where I'm going...
}
Obviously a problem in the making when you forget to register a map and wonder why your data isn't loading properly. So on to some reflection to auto-register:
public void Register(FluentMapConfiguration configuration)
{
var maps = GetType().Assembly.GetExportedTypes().Where(t =>
!t.IsAbstract &&
!t.IsInterface &&
t.BaseType is { IsGenericType: true } &&
t.BaseType.GetGenericTypeDefinition() == typeof(EntityMap<>)
).ToArray();
foreach (var map in maps)
{
var baseType = typeof(EntityMap<>);
var typeArguments = map.BaseType.GetGenericArguments();
var genericType = baseType.MakeGenericType(typeArguments);
var instance = Activator.CreateInstance(genericType);
configuration.AddMap((dynamic) instance);
}
}
but when it gets to the call to Activator.CreateInstance, it fails, with a MissingMethodException, Cannot create abstract class. It looks like it's trying to create an instance of EntityBase<TypeA> rather than TypeAMap, and since EntityBase<T> is an abstract class, I'm getting the error. So how can I construct my instances correctly?
The Where call filters out the types in the assembly and includes only types that are not abstract, not interfaces, and are direct subclasses of EntityMap<anything>. So maps contains the types TypeAMap, TypeBMap, TypeCMap etc.
Then for each of those, your code gets its base class and tries to instantiate that instead. See the annotated code in the for loop:
// suppose "map" is typeof(TypeAMap)
var baseType = typeof(EntityMap<>);
// typeArguments would be an array containing typeof(TypeA) only.
var typeArguments = map.BaseType.GetGenericArguments();
// genericType would be typeof(EntityMap<TypeA>)
var genericType = baseType.MakeGenericType(typeArguments);
// now you try to instantiate a EntityMap<TypeA>, and fails, because EntityMap<TypeA> is abstract
var instance = Activator.CreateInstance(genericType);
Since you want to do the reflection version of new TypeAMap(), new TypeBMap() etc, and we know that maps contain those types that you want to instantiate, you can just do:
foreach (var map in maps)
{
var instance = Activator.CreateInstance(map);
configuration.AddMap((dynamic) instance);
}
You don't need to care about the generic type parameters for EntityMap at all. The dynamic binder will figure that out when it tries to infer the type parameter for AddMap.
I have a class which uses generic properties. For example:
class Person
{
public MyGenericProperty<string> Field1
{
get { return field1; }
set { field1 = value; }
}
private MyGenericProperty<string> field1= new MyInheritedGenericProperty<string>("Alan1");
}
I want to use this class with reflection at another class and i have a method like that
public void DoSomethingWithProperty(object sourceobject)
{
foreach (var aProperty in sourceobject.GetType().GetProperties())
{
*if(aProperty.PropertyType == typeof(MyGenericProperty<>))*
{
*var obj = (MyGenericProperty<>)aProperty.GetValue(sourceobject, null);*
}
}
return null;
}
I have two problem
1- How can do type check of generic property. In that example code of if(aProperty.PropertyType == typeof(MyGenericProperty<>)) does not work.
2- T of MyGenericProperty could be any class and how can cast MyGenericProperty class without knowing T by reflection as
var obj = (MyGenericProperty<>)aProperty.GetValue(sourceobject, null);
Thank for helps.
Firstly, it's important to understand that you don't have a "generic property" - there's no such thing. You have a property whose type is a generic type... and that's not the same thing. (Compare that with a generic type or a generic method, each of which is genuinely generic in terms of introducing new type parameters.)
You can test it using this code:
if (aProperty.PropertyType.IsGenericType &&
aProperty.GetGenericTypeDefinition() == typeof(MyGenericProperty<>))
But as for the casting - it depends on what you want to do with the value afterwards. You may want to declare a non-generic base type of MyGenericProperty<> containing all the members which don't depend on the type parameter. I'd typically give that the same name as the generic type (e.g. MyGenericProperty) just without giving it type parameters. Then if you only need one of those members, you can use:
if (aProperty.PropertyType.IsGenericType &&
aProperty.GetGenericTypeDefinition() == typeof(MyGenericProperty<>))
{
var value = (MyGenericProperty) aProperty.GetValue(sourceObject, null);
// Use value
}
But then in that case you could use Type.IsAssignableFrom anyway:
if (typeof(MyGenericProperty).IsAssignableFrom(aProperty.PropertyType))
{
var value = (MyGenericProperty) aProperty.GetValue(sourceObject, null);
// Use value
}
If these hints don't help you, please give more details of what you're trying to do.
Assume I have a class like so:
public class MyClass<T>
{
public void Foo(T t)
{
}
}
Now, assume, I have an instance of MyClass<int> and a MethodInfo of its Foo method.
Calling methodInfo.GetParameters() will return a ParameterInfo array with one entry, referring to type int. My problem is, that I can't seem to find out, if that parameter was declared as int in the class or as T.
What am I trying to achieve?
At runtime, I want to read the documentation of the method specified by MethodInfo from the XML Doc file generated by Visual Studio.
For the above defined method, the key looks like this:
<namespace>.MyClass`1.Foo(`0)
The `0 refers to the first generic type parameter of the declaring class. To be able to construct this string, I need to somehow get this information.
But how? MethodInfo doesn't seem to contain that info...
The key seems to be Type.ContainsGenericParameters on the parameter type:
Given
public class MyClass<T>
{
public void Foo(T t)
{
}
public void Bar(int i)
{
}
}
Then
class Program
{
static void Main(string[] args)
{
var obj = new MyClass<int>();
// Closed type
var closedType = obj.GetType();
// Open generic (typeof(MyClass<>))
var openType = closedType.GetGenericTypeDefinition();
// Methods on open type
var fooT = openType.GetMethod("Foo");
var barint = openType.GetMethod("Bar");
// Parameter types
var tInFoo = fooT.GetParameters()[0].ParameterType;
var iInBar = barint.GetParameters()[0].ParameterType;
// Are they generic?
var tInFooIsGeneric = tInFoo.ContainsGenericParameters;
var iInBarIsGeneric = iInBar.ContainsGenericParameters;
Console.WriteLine(tInFooIsGeneric);
Console.WriteLine(iInBarIsGeneric);
Console.ReadKey();
}
}
outputs
True
False
This will obviously need more work for overloads and so on.
Could you get the definition of the generic class through Type.GetGenericTypeDefinition Method and find there the definition for the same method, say, by name (and the signature), and then compare Foo(T t) and Foo(int t):
MyClass<int> c = new MyClass<int>();
Type concreteType = c.GetType();
Console.Write("Concrete type name:");
Console.WriteLine(concreteType.FullName);
Console.WriteLine();
MethodInfo concreteMethod = concreteType.GetMethod("Foo");
if (concreteMethod != null)
{
Console.WriteLine(concreteMethod.Name);
foreach (ParameterInfo pinfo in concreteMethod.GetParameters())
{
Console.WriteLine(pinfo.Name);
Console.WriteLine(pinfo.ParameterType);
Console.WriteLine();
}
Console.WriteLine();
}
if (concreteType.IsGenericType)
{
Console.Write("Generic type name:");
Type genericType = concreteType.GetGenericTypeDefinition();
Console.WriteLine(genericType.FullName);
Console.WriteLine();
MethodInfo genericMethod = genericType.GetMethod("Foo");
if (genericMethod != null)
{
Console.WriteLine(genericMethod.Name);
foreach (ParameterInfo pinfo in genericMethod.GetParameters())
{
Console.WriteLine(pinfo.Name);
Console.WriteLine(pinfo.ParameterType);
Console.WriteLine();
}
Console.WriteLine();
}
}
I don't know if you have considered using Mono.Cecil instead of .Net's reflection.
// Gets the AssemblyDefinition (similar to .Net's Assembly).
Type testType = typeof(MyClass<>);
AssemblyDefinition assemblyDef = AssemblyDefinition.ReadAssembly(new Uri(testType.Assembly.CodeBase).LocalPath);
// Gets the TypeDefinition (similar to .Net's Type).
TypeDefinition classDef = assemblyDef.MainModule.Types.Single(typeDef => typeDef.Name == testType.Name);
// Gets the MethodDefinition (similar to .Net's MethodInfo).
MethodDefinition myMethodDef = classDef.Methods.Single(methDef => methDef.Name == "Foo");
then myMethodDef.FullName returns
"System.Void MyNamespace.MyClass`1::Foo(System.Int32,T,System.String)"
and classDef.GenericParameters[0].FullName returns
"T"
Note that Mono.Cecil uses a different way of writing generics, nested classes and arrays:
List[T] => List<T>
MyClass+MyNestedClass => MyClass/MyNestedClass
int[,] => int[0...,0...]
This was a tricky one. If you have a MethodInfo instance of a (non-generic) method, which was obtained on an open generic Type, you can actually use it to repeatedly close the method (i.e., with different generic arguments for the enclosing type).
The key is to use the obscure static function MethodInfo.GetMethodFromHandle(...):
Complete working example:
static class MyType<T>
{
public static int TheMethod() => typeof(T).MetadataToken; // demo method
};
How to use:
static void demo()
{
var Topen = typeof(MyType<>);
var mi_open = Topen.GetMethod("TheMethod"); // can't Invoke() this one...
// ...later perhaps... MyType<ushort>.TheMethod()
var Tclose = Topen.MakeGenericType(typeof(ushort));
var mi = MethodInfo.GetMethodFromHandle(mi_open.MethodHandle, Tclose.TypeHandle);
var ret = (int)mi.Invoke(null, null); // works --> 0x02000150
// later yet... MyType<Guid>.TheMethod(), reusing 'mi_open' (and Topen)...
Tclose = Topen.MakeGenericType(typeof(Guid));
mi = MethodInfo.GetMethodFromHandle(mi_open.MethodHandle, Tclose.TypeHandle);
ret = (int)mi.Invoke(null, null); // works --> 0x020000eb
}
Notice that the Tclose type handle (passed in as the second arg) is not simply the handle of the type argument T that you ultimately want to parametrize with. Rather, you have to use that T to close the original Topen (open) generic type first, and then use the handle for the resulting (closed) generic type.
In other words, you can't close a method without first explicitly closing its generic type.
public class MyList : List<MyClass>
How can I get the type MyClass through reflection if I have an object that contains an instance of MyList? The list can be empty, so I can't do something like myList[0].GetType().
p.s. I can't just stop using MyList and directly use the generic List instead (the situation is a bit more complicated, and there are reasons for "hiding" the generic argument), so I can't pick up MyClass through GetGenericArguments().
var elementType = (
from iface in myList.GetType().GetInterfaces()
where iface.IsGenericType
where iface.GetGenericTypeDefinition() == typeof(IList<>)
select iface.GetGenericArguments()[0])
.Single();
I use IList<T> instead of list. This is more generic. However, there is also the change of a type implementing multiple ILis<T> versions (such as IList<string> and IList<int>).
You can get the base type, which will be List<MyClass>; from it you can get the generic type argument with GetGenericArguments.
MyList list = new MyList();
Type baseTypeGenericArgument = list.GetType().BaseType.GetGenericArguments()[0];
string argumentTypeName = baseTypeGenericArgument.GetType().FullName;
Is your class implements generic interfaces? you can use the following code:
Type argument = GetGenericArgument(typeof(MyList), typeof(IList<>));
//...
static Type GetGenericArgument(Type type, Type genericTypeDefinition) {
Type[] interfaces = type.GetInterfaces();
for(int i = 0; i < interfaces.Length; i++) {
if(!interfaces[i].IsGenericType) continue;
if(interfaces[i].GetGenericTypeDefinition() == genericTypeDefinition)
return interfaces[i].GetGenericArguments()[0];
}
return null;
}
Without interfaces you can try the following:
class A { }
class B { }
class G<T> { }
class G1<T> : G<A> { }
class G2 : G1<B> { }
//...
Type argument1 = GetGenericArgument(typeof(G2)); // B
Type argument2 = GetGenericArgument(typeof(G2),1 ); // A
//...
static Type GetGenericArgument(Type type, int level = 0) {
do {
if(type.IsGenericType && 0 == level--)
return type.GetGenericArguments()[0];
type = type.BaseType;
}
while(type != null);
return null;
}
I need some help figure out how to use reflection to get the concrete implementation based of the Dto type:
public interface IDocumentService<TDto>
{
}
public interface ICommentService: IDoumentService<CommentDto>
{
}
public abstract class DocumentService<TEntity,TDto>: IDocumentService<TDto> where TEntity: Entity, where TDto: Dto
{
}
public class CommentService: DocumentService<Comment,CommentDto>, ICommentService
{
}
So, what I want to do, is pass the CommentDto to a method so I can get to the CommentService.
public IDocumentService<TDto> GetDocumentService<TDto>()
{
//based on the TDto type I want to find the concrete that
//implements IDocumentService<TDto>
}
I would call it like this:
var commentDocumentService = GetDocumentService<CommentDto>();
So, I would get back CommentService, knowing that I would only see the methods part of the IDocumentService interface.
Here is a possible implementation for GetDocumentService.
public static IDocumentService<TDto> GetDocumentService<TDto>()
{
// Gets the type for IDocumentService
Type tDto=typeof(IDocumentService<TDto>);
Type tConcrete=null;
foreach(Type t in Assembly.GetExecutingAssembly().GetTypes()){
// Find a type that implements tDto and is concrete.
// Assumes that the type is found in the executing assembly.
if(tDto.IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface){
tConcrete=t;
break;
}
}
// Create an instance of the concrete type
object o=Activator.CreateInstance(tConcrete);
return (IDocumentService<TDto>)o;
}
It wasn't clear whether you wanted to return a new object, so I assumed so.
EDIT:
Due to your comment, here is a modified version of GetDocumentService. The disadvantage is that you need to specify another type parameter. The advantage, though, is that this approach provides a certain degree of type safety, since both type parameters must be compatible.
public static T GetDocumentService<TDto, T>() where T : IDocumentService<TDto>
{
// Gets the type for IDocumentService
Type tDto=typeof(T);
Type tConcrete=null;
foreach(Type t in Assembly.GetExecutingAssembly().GetTypes()){
// Find a type that implements tDto and is concrete.
// Assumes that the type is found in the calling assembly.
if(tDto.IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface){
tConcrete=t;
break;
}
}
// Create an instance of the concrete type
object o=Activator.CreateInstance(tConcrete);
return (T)o;
}
EDIT 2:
If I understand correctly, you want to get the other interfaces implemented by the type of the return value of GetDocumentService. For example, GetDocumentService<CommentDto> returns an object of type CommentService that implements the ICommentService interface. If I understand correctly, the return value should be a Type object (for example, the return value could be typeof(ICommentService)). Once you have the type, you should call the type's FullName property to get the type's name.
Use the following method on the return value of GetDocumentService to get the type of interface implemented by that value, for instance, typeof(ICommentService).
public static Type GetDocumentServiceType<TDto>(IDocumentService<TDto> obj){
Type tDto=typeof(IDocumentService<TDto>);
foreach(Type iface in obj.GetType().GetInterfaces()){
if(tDto.IsAssignableFrom(iface) && !iface.Equals(tDto)){
return iface;
}
}
return null;
}
Firstly, your CommentService class needs to be discoverable somehow, given the type of TDto. You can search all the loaded types from all the assemblies in the current AppDomain - however that will be painfully slow.
So you have the following viable options:
Use an attribute on the assembly that defines CommentService.
Use configuration to define this information.
Use MEF.
I'll demonstrate the first approach. Firstly create the attribute:
[AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)]
public sealed class DtoProviderAttribute : Attribute
{
public Type ProvidedType { get; private set; }
public Type ProviderType { get; private set; }
public DtoProviderAttribute(Type providedType, Type providerType)
{
ProvidedType = providedType;
ProviderType = providerType;
}
}
And then apply it to the assembly that defines CommentService (typically you would put in AssemblyInfo.cs).
[assembly:DtoProvider(typeof(CommentDto), typeof(CommentService))]
Now you can use those attributes to search for the concrete implementations.
public class ServiceFactory
{
private static readonly Dictionary<RuntimeTypeHandle, Func<object>> _dtoMappings = new Dictionary<RuntimeTypeHandle, Func<object>>();
public static IDocumentService<TDto> GetDocumentService<TDto>()
{
var rth = typeof(TDto).TypeHandle;
Func<object> concreteFactory;
lock (_dtoMappings)
{
if (_dtoMappings.TryGetValue(typeof(TDto).TypeHandle, out concreteFactory))
return (IDocumentService<TDto>)concreteFactory();
FillMappings();
if (!_dtoMappings.TryGetValue(typeof(TDto).TypeHandle, out concreteFactory))
throw new Exception("No concrete implementation found.");
return (IDocumentService<TDto>)concreteFactory();
}
}
private static void FillMappings()
{
// You would only need to change this method if you used the configuration-based approach.
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
var attrs = assembly.GetCustomAttributes(typeof(DtoProviderAttribute), false);
foreach (DtoProviderAttribute item in attrs)
{
if (!_dtoMappings.ContainsKey(item.ProvidedType.TypeHandle))
{
var expr = Expression.Lambda<Func<object>>(Expression.Convert(Expression.New(item.ProviderType), typeof(object)));
_dtoMappings.Add(item.ProvidedType.TypeHandle, expr.Compile());
}
}
}
}
}
As 'Rune' pointed out: because of the cache the overhead of searching all the assemblies is low:
private static void FillMappings()
{
foreach (var type in AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()).Where(x => x.IsClass && !x.IsAbstract))
{
foreach (var iface in type.GetInterfaces().Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDocumentService<>)))
{
var arg = iface.GetGenericArguments()[0];
if (!_dtoMappings.ContainsKey(arg.TypeHandle))
{
var expr = Expression.Lambda<Func<object>>(Expression.Convert(Expression.New(type), typeof(object)));
_dtoMappings.Add(arg.TypeHandle, expr.Compile());
}
}
}
}
another possibility:
public IDocumentService<TDto> GetDocumentService<TDto>()
{
var genericParameter = typeof(TDto);
return (from type in Assembly.GetExecutingAssembly().GetTypes() // Get Types
where type.GetConstructor(Type.EmptyTypes) != null // That is concrete
let interfaces = type.GetInterfaces()
from intf in interfaces
where intf.IsGenericType // Which implement generic interface
let genarg = intf.GetGenericArguments()[0]
where genarg == genericParameter // Where generic argument is of type genericParameter
select (IDocumentService<TDto>) // Cast to IDocumentService
Activator.CreateInstance(type)).FirstOrDefault(); // Instantiate
}