在不知道类型的情况下引用泛型类型的实例



我有一个这样的泛型类:

public class Foo<T>
{
    public string SomeMethod();
    ...
}

我希望存储对这种类型的不同通用实例的引用列表。例如

List<Foo<object>> foos = new List<Foo<object>>();
foos.Add(new Foo<string>());
foos.Add(new Foo<int>());
foos.Add(new Foo<Person>());
// Then do some processing like ...
foreach (var Foo<object> in foos)
{
     Console.WriteLine(foo.SomeMethod());
}
etc...

foos 上的编译器错误。添加呼叫说:

"Cannot convert from 'Foo<string>' to 'Foo<object>'

如何存储类型不同的泛型类型实例列表?

我不想只存储对象列表(ArrayList、List、List 等),并且必须使用反射来访问它们的成员!

DoSomething()不需要

Foo<T>中的T。因此,您可以通过创建用于DoSomething()的接口并将对象存储在该接口的List<>中来轻松解决问题:

public interface ISomethingDoer {
    void DoSomething();
}
public class Foo<T> : ISomethingDoer {
    public void DoSomething() { }
}

和-

List<ISomethingDoer> foos = new List<ISomethingDoer>();
foos.Add(new Foo<string>());
foos.Add(new Foo<int>());
foos.Add(new Foo<Person>());
// Then do some processing like ...
foreach (var foo in foos)
{
     Console.WriteLine(foo.DoSomething());
}

你想创建一个新的接口,把你的 SomeMethod() 放进去,让你的泛型实现它。这是最简单的解决方案。这就是它们存在的原因。

如果你的 SomeMethod 依赖于 T,那么你需要研究接口协方差和逆变。

class Program
{
    public interface IFoo
    {
        void DoSomething();
    }
    public interface IGenericFoo<out T> : IFoo
    {
        T GetDefault();
    }

    public class Foo<T> : IGenericFoo<T>
    {
        public T GetDefault()
        {
            return default(T);
        }
        public void DoSomething()
        {
            Console.WriteLine("Meep!");
        }
    }

    private static void Main()
    {
        var fooCollection = new List<IFoo>
        {
            new Foo<string>(), 
            new Foo<StringBuilder>(),
            new Foo<int>()
        };
        foreach (var instance in fooCollection)
            instance.DoSomething();
        // Covariance example
        var fooCollectionGenericI = new List<IGenericFoo<object>>
        {
            new Foo<string>(), 
            new Foo<StringBuilder>(),
            // new Foo<int>() not possible since covariance is not supported on structs :( 
        };
        foreach (var instance in fooCollectionGenericI)
        {
            var wxp = instance.GetDefault();
            Console.WriteLine(wxp == null ? "NULL" : wxp.ToString());
        }
        Console.ReadLine();
    }
}

在某些情况下,这种协方差可能非常有用。

最新更新