简体   繁体   中英

Referencing instances of Generic Type without knowing the Type

I have a generic class like so:

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

I wish to store a list of references to different generic instances of this type. eg

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...

Compiler errors on foos.Add call saying:

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

How can I store a list of generic type instances where the types differ?

I don't want to have to just store a list of objects (ArrayList, List, List, etc) and have to use reflection to access their members!

The T in Foo<T> is not needed to DoSomething() . So you can easily solve the problem by creating an interface for DoSomething() and store the objects in a List<> of that interface:

public interface ISomethingDoer {
    void DoSomething();
}

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

And -

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

You want to create a new interface, and put your SomeMethod() in it, and let your generic implement it. It's the easiest solution. That's why they exists.

If your SomeMethod depends on T, then you need to look into interface covariance and contravariance.

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

This covariance can be very useful in some cases.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM