【问题标题】:Referencing instances of Generic Type without knowing the Type在不知道类型的情况下引用通用类型的实例
【发布时间】:2013-11-10 22:18:34
【问题描述】:

我有一个像这样的通用类:

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 等)并且必须使用反射来访问它们的成员!

【问题讨论】:

  • 如果要在其中存储不同的类型,为什么还需要一个通用列表?你可以只使用一个普通的 ArrayList
  • 因为我希望访问 Foo 类的成员并且不想使用反射来这样做。我将编辑我的问题以使其更清晰
  • @BenDaniel 那你为什么不让 Foo 成为一个非泛型类呢?
  • @Kenneth Foo 有其他返回 T 实例的方法和属性。我想保持 Foo 通用。

标签: c# generics


【解决方案1】:

Foo&lt;T&gt; 中的T 不需要DoSomething()。因此,您可以通过为DoSomething() 创建一个接口并将对象存储在该接口的List&lt;&gt; 中来轻松解决问题:

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());
}

【讨论】:

  • 接口是通往这里的路。请记住,您必须将任何特定于类型的逻辑放在 Foo 类中。这是我花了一段时间才掌握的心态:您必须以这样一种方式设计您的应用程序,即类型特定的逻辑可以在泛型类中完成。
  • 感谢@Keith(&@Moeri)。是的,这确实可能是我应该做的事情。我需要多考虑一下我的实际(更复杂)用例,看看这是否符合我的要求。
【解决方案2】:

您想创建一个新接口,并将您的 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();
    }
}

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

【讨论】:

  • 您能否详细说明一下协变和逆变。我确实对此进行了研究,但无法理解它并且无法编译任何代码。 ://
  • 我添加了一些代码,它演示了如何保持一切通用性,同时仍然通过接口进行。严重的缺陷是它不适用于结构。主要是因为这是协方差支持的:类、委托..*等待解释*
  • 优秀。似乎我无法让我的协方差代码工作的原因是因为我正在使用像 int 这样的值类型来测试它们。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-18
  • 2012-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-21
相关资源
最近更新 更多