Is there a way to cast generic lists to lists of interface/base class types?

谁说我不能喝 提交于 2019-12-21 20:54:22

问题


I'm trying to show someone a use for interfaces in a crazy situation they've created. They have several unrelated objects in lists, and need to perform an operation on two string properties in each object. I'm pointing out that if they define the properties as part of an interface, they can use the interface object as the type for a method parameter that acts on it; for example:

void PrintProperties(IEnumerable<ISpecialProperties> list)
{
    foreach (var item in list)
    {
        Console.WriteLine("{0} {1}", item.Prop1, item.Prop2);
    }
}

This seems like it's all good, but the lists that need to be worked on aren't (and shouldn't) be declared with the interface as the type parameter. However, it doesn't seem like you can cast to a different type parameter. For example, this fails and I can't understand why:

using System;
using System.Collections.Generic;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Test> myList = new List<Test>();
            for (int i = 0; i < 5; i++)
            {
                myList.Add(new Test());
            }

            PrintList((IEnumerable<IDoSomething>)myList);
        }

        static void PrintList(IEnumerable<IDoSomething> list)
        {
            foreach (IDoSomething item in list)
            {
                item.DoSomething();
            }
        }
    }

    interface IDoSomething
    {
        void DoSomething();
    }

    public class Test : IDoSomething
    {
        public void DoSomething()
        {
            Console.WriteLine("Test did it!");
        }
    }
}

I can use the Enumerable.Cast<T> member to do this, but I was looking for a method that might work in .NET 2.0 as well. It seems like this should be possible; what am I missing?


回答1:


The problem is with the method, not with how it's called.....

void PrintProperties<SP>(IEnumerable<SP> list) where SP: ISpecialProperties
{
    foreach (var item in list)
    {
        Console.WriteLine("{0} {1}", item.Prop1, item.Prop2);
    }
}



回答2:


The reason it fails is because generics don't exhibit variance in C# (yet).

As for the fix for IEnumerable<T>, however, try this:

public static IEnumerable<TBase> SafeConvert<TBase, TDerived>(IEnumerable<TDerived> source)
    where TDerived : TBase
{
    foreach (TDerived element in source)
    {
        yield return element; // Implicit conversion to TBase
    }
}

EDIT: The other existing answer is better than the above for this particular case, but I'll leave this here as a generally useful thing if you do actually need to "convert" the sequence.




回答3:


You can just use a foreach on the lists you have. The foreach does a built in cast. So if you take the loop out of the function you can write something like

List<Test> myList = new List<Test>();
for (int i = 0; i < 5; i++)
{
   myList.Add(new Test());
}

foreach (IDoSomething item in myList)
{
   item.DoSomething();
}



回答4:


What you want is called "interface covariance" and is not supported by C# at the moment. You can read about it on Eric Lippert's blog.




回答5:


This doesn't answer your question (or the point of the exercise I guess :), but I'd just use reflection in this case by attaching special attributes to the properties of interest.



来源:https://stackoverflow.com/questions/188769/is-there-a-way-to-cast-generic-lists-to-lists-of-interface-base-class-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!