传递已实现的模板化类型:无法从实现转换为接口

时间:2013-02-08 04:04:59

标签: c# generics

有人可以向我解释为什么在C#中这是不正确的:

namespace NamespaceA
{
    public class ClassA<T1>
    {
        T1 mT1;
        public T1 Type1
        {
            get { return mT1; }
        }
    }

    public class IOForClassA
    {
        public interface ICanOutput
        {
            void OutputFunction();
        }

        public static void Output(ClassA<ICanOutput> aClassA_WithOutputCapabilities)
        {
            aClassA_WithOutputCapabilities.Type1.OutputFunction();
        }
    }
}

namespace NamespaceB
{
    public class ClassB
    {
        public class OutputableClassA : NamespaceA.IOForClassA.ICanOutput
        {
            public void OutputFunction()
            {
            }
        }
        public ClassB()
        {
            NamespaceA.ClassA<OutputableClassA> aOutputableA = new NamespaceA.ClassA<OutputableClassA>();
            NamespaceA.IOForClassA.Output(aOutputableA);
        }
    }
}

这将导致编译错误:

参数1:无法转换为'NamespaceA.ClassA'&lt;“NamespaceB.ClassB.OutputableClassA”&gt;“ to NamespaceA.ClassA“&lt;”NamespaceA.IOForClassA.ICanOutput“&gt;”

...但是NamespaceB.ClassB.OutputableClassA实现了NameSpaceA.IoForClassA.ICanOutput,所以我不明白为什么这是一个问题......

我试图允许用户创建他/她希望的任何类型的ClassA。但是,如果他们希望ClassA是“可输出的”,那么其模板化类型必须实现特定的接口。

3 个答案:

答案 0 :(得分:2)

您正遇到covariance/contravariance问题。基本上,只有接口可以允许泛型类型具有更多派生类型,并且只有在使用输入/输出关键字明确指定它们时(在链接文章中概述)。

阅读你的代码,似乎你想要在不知道其类型的情况下调用类的已知命名成员,这可能是type constraints可能更适合的类型。你可能不得不稍微改变你的设计。

namespace NamespaceA
{
    public class ClassA<T1> where T1 : IOForClassA.ICanOutput
    {
        T1 mT1;
        public T1 Type1
        {
            get { return mT1; }
        }
    }

    public class IOForClassA
    {
        public interface ICanOutput
        {
            void OutputFunction();
        }

        public static void Output<T>(ClassA<T> aClassA_WithOutputCapabilities) where T : IOForClassA.ICanOutput
        {
            aClassA_WithOutputCapabilities.Type1.OutputFunction();
        }
    }
}

namespace NamespaceB
{
    public class ClassB
    {
        public class OutputableClassA : NamespaceA.IOForClassA.ICanOutput
        {
            public void OutputFunction()
            {
            }
        }
        public ClassB()
        {
            NamespaceA.ClassA<OutputableClassA> aOutputableA = new NamespaceA.ClassA<OutputableClassA>();
            NamespaceA.IOForClassA.Output(aOutputableA);
        }
    }
}

答案 1 :(得分:0)

OutputableClassA源自ICanOutput。但这并不意味着ClassA<OutputableClassA>也来自ClassA<ICanOutput>。这就是你的代码无效的原因。

链接Covariance and Contravariance in Generics可能会有所帮助。

答案 2 :(得分:0)

@syazdani关于它是一个协方差问题是正确的(他链接到一篇有用的文章),但他的例子并没有使用协方差,这将实现你正在寻找的东西。虽然他的答案是正确的(并且可能是优选的,因为它带来了额外的好处,如果您需要将其带入Output方法,我会告诉您如何使用协变来实现这一点界面,以防它可以帮助您在任何其他情况下使用它们:

namespace NamespaceA
{
    public interface IClassA<out T1>
    {
        T1 Type1 { get; }
    }

    public class ClassA<T1> : IClassA<T1>
    {
        T1 mT1;
        public T1 Type1
        {
            get { return mT1; }
        }
    }

    public class IOForClassA
    {
        public interface ICanOutput
        {
            void OutputFunction();
        }

        public static void Output(IClassA<ICanOutput> aClassA_WithOutputCapabilities)
        {
            aClassA_WithOutputCapabilities.Type1.OutputFunction();
        }
    }
}

namespace NamespaceB
{
    public class ClassB
    {
        public class OutputableClassA : NamespaceA.IOForClassA.ICanOutput
        {
            public void OutputFunction()
            {
            }
        }
        public ClassB()
        {
            NamespaceA.ClassA<OutputableClassA> aOutputableA = new NamespaceA.ClassA<OutputableClassA>();
            NamespaceA.IOForClassA.Output(aOutputableA);
        }
    }
}