Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/svg/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 列表<&燃气轮机;Func<&燃气轮机;s、 使用泛型返回类型编译错误,但为什么?_C#_Generics_.net 4.0_Func - Fatal编程技术网

C# 列表<&燃气轮机;Func<&燃气轮机;s、 使用泛型返回类型编译错误,但为什么?

C# 列表<&燃气轮机;Func<&燃气轮机;s、 使用泛型返回类型编译错误,但为什么?,c#,generics,.net-4.0,func,C#,Generics,.net 4.0,Func,这是一个有点长的问题,请耐心听我说 我需要在一组字符串和每个字符串对应的泛型方法调用之间创建一个映射。不过,我遇到了一个编译问题,下面解释 在我的场景中,我使用的是字典,但对于列表,同样存在这个问题。为了简单起见,我在下面的示例中使用了一个列表 考虑以下三类: public abstract class MyBase { /* body omitted */ } public class MyDerived1 : MyBase { /* body omitted */ } public cl

这是一个有点长的问题,请耐心听我说

我需要在一组字符串和每个字符串对应的泛型方法调用之间创建一个映射。不过,我遇到了一个编译问题,下面解释

在我的场景中,我使用的是
字典
,但对于
列表
,同样存在这个问题。为了简单起见,我在下面的示例中使用了一个
列表

考虑以下三类:

public abstract class MyBase { /* body omitted */  }
public class MyDerived1 : MyBase { /* body omitted */  }
public class MyDerived2 : MyBase { /* body omitted */  }
以及其他类中的方法:

public class Test
{
    public T GetT<T>() where T : MyBase { /* body omitted */ }
}
公共类测试
{
public T GetT(),其中T:MyBase{/*body省略了*/}
}
在另一个类中,我可以这样声明
列表

public class SomeClass
{
    public void SomeFunc()
    {
        var test = new Test();

        var list1 = new List<Func<MyBase>>
            {
                test.GetT<MyDerived1>,
                test.GetT<MyDerived2>
            };
    }
}
public class RetVal<T> where T : MyBase { /* body omitted */ }

public class Test
{
    public RetVal<T> GetRetValT<T>() where T : MyBase
    {
        return null;
    }
}
公共类SomeClass
{
public void SomeFunc()
{
var测试=新测试();
var list1=新列表
{
test.GetT,
test.GetT
};
}
}
这一切都很好

但是,如果我想要一个函数返回这样的泛型类,该怎么办

public class SomeClass
{
    public void SomeFunc()
    {
        var test = new Test();

        var list1 = new List<Func<MyBase>>
            {
                test.GetT<MyDerived1>,
                test.GetT<MyDerived2>
            };
    }
}
public class RetVal<T> where T : MyBase { /* body omitted */ }

public class Test
{
    public RetVal<T> GetRetValT<T>() where T : MyBase
    {
        return null;
    }
}
公共类RetVal,其中T:MyBase{/*body省略*/}
公开课考试
{
public RetVal GetRetValT()其中T:MyBase
{
返回null;
}
}
我想用这个函数创建一个等价的
列表
。i、 e.列表>>

public class Class1
{
    public void SomeFunc()
    {
        var test = new Test();

        var list2 = new List<Func<RetVal<MyBase>>>
            {
                test.GetRetValT<MyDerived1>, // compile error
                test.GetRetValT<MyDerived2> // compile error
            };
    }
}
公共类1
{
public void SomeFunc()
{
var测试=新测试();
var list2=新列表
{
test.GetRetValT,//编译错误
test.GetRetValT//编译错误
};
}
}
我得到
的编译错误,应该是带有'RetVal GetRetValT()'签名的方法


那么,有没有办法解决这个问题,或者有没有其他方法可以用来创建字符串…泛型方法调用映射?

我最近遇到了类似的问题

C#不支持用于接口实现或虚拟方法覆盖的返回类型协方差。有关详细信息,请参见此问题:

你也许能破解这个,我做:

public RetVal<R> GetRetValT<T,R>() where T : MyBase where R : MyBase
{
    return null;
}

//Then, change this to:
test.GetRetValT<MyDerived1, MyBase>
public RetVal GetRetValT()其中T:MyBase其中R:MyBase
{
返回null;
}
//然后,将此更改为:
test.GetRetValT

问题在于泛型的经典协方差/逆方差。您假设,因为
MyDerived1
MyDerived2
继承自
MyBase
,所以
RetVal
继承自
RetVal
,而不是

解决此问题的最简单方法可能是将代码更改为:

var list2 = new List<Func<RetVal<MyBase>>>
        {
            () => (MyBase)test.GetRetValT<MyDerived1>,
            () => (MyBase)test.GetRetValT<MyDerived2>
        };
var list2=新列表
{
()=>(MyBase)test.GetRetValT,
()=>(MyBase)test.GetRetValT
};
或者更好,正如JS在评论中指出的那样,如果可能,只需将
RetVal
更改为协变:

public interface IRetVal<out T> { ... }

public class RetVal<T> : IRetVal<T> { ... }
公共接口IRetVal{…}
公共类检索:IRetVal{…}
C#仅允许接口上的协方差。这意味着您不能自动将
RetVal
转换为
RetVal
。如果
RetVal
应该是协变的,则为其创建一个接口,如下所示:

public interface IRetVal<out T>
{

}
public class RetVal<T> : IRetVal<T> where T : MyBase { /* body omitted */ }

public class Test
{
    public IRetVal<T> GetRetValT<T>() where T : MyBase
    {
        return null;
    }
}
公共接口IRetVal
{
}
公共类RetVal:IRetVal,其中T:MyBase{/*body省略*/}
公开课考试
{
public IRetVal GetRetValT(),其中T:MyBase
{
返回null;
}
}
然后,此代码将起作用:

    var list2 = new List<Func<IRetVal<MyBase>>>
        {
            test.GetRetValT<MyDerived1>,
            test.GetRetValT<MyDerived2>
        };
var list2=新列表
{
test.GetRetValT,
test.GetRetValT
};

类的泛型类型参数不能是协变的,但接口的泛型类型参数可以是协变的。如果类型参数声明为协变,则可以使用接口
IRetVal
而不是类
RetVal
,执行所需操作。为了将接口类型参数声明为协变,必须仅在“输出”位置使用它

为了举例说明,此代码不会编译:

interface IRetVal<out T>
{
    T Value { get; }
    void AcceptValue(T value);
}

这是一个差异问题。基本上,
RetVal
不是
RetVal
。当我有时间的时候,会有更多的细节。@JonSkeet说的是真的。这是C#中的一条定律。如果要将
RetVal
的MyDerivated转换为
RetVal
的MyBase,则必须使
RetVal
也接受非类型的泛型,即
RetVal
。请看这里的一个例子:我可能完全偏离了这一点,但返回泛型的想法是否违背了泛型必须(最终)在编译时解析为编译器知道的内容的概念?也就是说,编译器抛出这个错误的原因是它希望您提供可以返回特定类型的方法(我意识到这与您尝试执行的相反)。正如我所说的,只是一个观察,可能离我们的基地太远了…@DavidW:我不知道你在说什么。你是说一个
Func
不应该被转换成
Func
?正如前两位评论者所说(比我更有效),RetVal不是RetVal,尽管我们在OP的层次结构中理解MyDerived是一个MyBase。不完全-在C#4中,
Func
在t中是协变的,这就是第一个示例工作的原因。这是
RetVal
,在这里是不可协变的。对了,对不起。。。如果您在另一个AppDomain:
AppDomain中使用
dataSources=new List
,将解决这个问题。Create
@Kiquenet:我不理解您的评论。你能澄清一下吗?