C#使用Type参数将对象转换为通用接口

时间:2018-02-26 10:04:11

标签: c# generics reflection

我想将对象转换为通用自定义接口。

这是我的界面:

public interface IContainer<T>
{
    IEnumerable<T> SaveToCache(IEnumerable<T> models);
}

public class Container<T>
{
    IEnumerable<T> SaveToCache(IEnumerable<T> models);
}

我在Serviceclass中使用此接口:

public class Manager()
{
    public Manager()
    {
        address = new Container<Address>(this);
        // more containers are constructed here...
    }

    private static Container<Address> address;
    public static Container<Address> Address => address;

    // here are more properties of Type Container<T>
}

现在我想动态调用SaveToCacheAsync方法:

private void SaveItems(IEnumerable<object> items, string typeName)
{
    object container = typeof(Manager)
        .GetRuntimeProperty(typeName).GetValue(null, null);
    var t = Type.GetType($"MyNameSpace.Models.{typeName}");

    // variant A - doesn't work, but makes clear what I want to do
    (container as IContainer<t>).SaveToCache(items);

    // variant B - doesn't work either
    var saveMethod = container.GetType()
        .GetRuntimeMethod("SaveToCache", new Type[] { Type.GetType($"System.Collections.Generic.List`1[{t.FullName}]") })
        .MakeGenericMethod(new Type[] { t });

    saveMethod.Invoke(container, new object[] { });
}

该项目是PCL,因此我使用了GetRuntimeMethods

1 个答案:

答案 0 :(得分:0)

你的第二个版本不起作用,因为该方法本身不是通用的,类是,并且你拥有的Type实例已经是一个实例化的泛型类型,因为你从它形成了一个对象实例。虽然这个版本有效,但它并不理想,因为它涉及使用反射调用方法,这种方法很慢并且通常看起来像代码气味

var saveMethod = container.GetType()
    .GetRuntimeMethod("SaveToCache", new Type[] { typeof(IEnumerable<>).MakeGenericType(t) })
    .Invoke (container, new object[] { items });

更好的方法是使用非通用版本的界面(非常类似于IEnumerable<T>IEnumerable

public interface IContainer
{
    IEnumerable SaveToCache(IEnumerable models);
}
public interface IContainer<T> : IContainer
{
    IEnumerable<T> SaveToCache(IEnumerable<T> models);
}

您的类可以显式实现IContainer以避免调用非泛型方法,并仅在SaveItems方法的上下文中使用它

public class Container<T> : IContainer<T>
{
    public IEnumerable<T> SaveToCache(IEnumerable<T> models)
    {
        return models;
    }

    IEnumerable IContainer.SaveToCache(IEnumerable models)
    {

    }
}

var container = new Container<string>();
container.SaveToCache(new string[] { "" }); // The generic method is avaiable if we have an referecne to the class
container.SaveToCache(new int[] { 0 });// And this will be a compile time error as expected

IContainer icontainer = container;
icontainer.SaveToCache(new string[] { "" }); // The non genric method will be called 
icontainer.SaveToCache(new int[] { 0 });// And this will be a runtime time error