Reputation: 2318
I have two storages. The first is simple. The second add additional info to entity (meta). I want to write factory that create storage based on generic type of my entity. But i can't do it. I received a compilation error. Maybe i want some weird things and i should rewrite architecture. Also i tried to use reflection, but it also didn't work. Here is an example:
using System;
using System.Reflection;
namespace ConsoleApp5
{
interface IStorage<T>
{
void Save();
}
class Storage<T> : IStorage<T>
{
public virtual void Save()
{
Console.WriteLine("Save");
}
}
class StorageWithMeta<T> : Storage<T> where T : EntityWithMeta
{
public override void Save()
{
Console.WriteLine("Save With Meta");
}
}
abstract class EntityWithMeta
{
}
class StorageFactory
{
public static IStorage<T> Create<T>()
{
if (typeof(EntityWithMeta).IsAssignableFrom(typeof(T)))
{
return CreateWithMeta<T>(); //compilation error! (type T must be convertible to EntityWithMeta)
//reflection based approach:
//var methodInfo = typeof(StorageFactory).GetMethod("CreateWithMeta", BindingFlags.Static | BindingFlags.NonPublic);
//return (IStorage<T>) methodInfo.Invoke(null, null); //System.InvalidOperationException. ContainsGenericParameters is true
}
return new Storage<T>();
}
private static IStorage<T> CreateWithMeta<T>() where T : EntityWithMeta
{
return new StorageWithMeta<T>();
}
}
class MyClass1
{
}
class MyClass2 : EntityWithMeta
{
}
class EntryPoint
{
public static void Main()
{
StorageFactory.Create<MyClass1>().Save();//expected Save
StorageFactory.Create<MyClass2>().Save();//expected Save With Meta
}
}
}
Upvotes: 2
Views: 82
Reputation: 12799
The problem is that the compiler cannot guarantee that T
is assignable to EntityWithMeta
, despite your runtime check. In other words it has no idea what that if
statement means in context. You can get around this by using Activator.CreateInstance and Type.MakeGenericType
return (IStorage<T>)Activator.CreateInstance(typeof(StorageWithMeta<>).MakeGenericType(typeof(T)));
A note on your reflection based approach (which you commented out in the OP): the method CreateWithMeta
is itself generic. The MethodInfo
object you have is for a generic method definition. You were on the right track but you have to create the constructed generic method using MethodInfo.MakeGenericMethod:
var methodInfo = typeof(StorageFactory).GetMethod("CreateWithMeta", BindingFlags.Static | BindingFlags.NonPublic);
var constructedGeneric = methodInfo.MakeGenericMethod(typeof(T));
return (IStorage<T>)constructedGeneric.Invoke(null, null);
Upvotes: 2