委托“错误的返回类型”
本文关键字:返回类型 错误 委托 | 更新日期: 2023-09-27 18:31:39
一方面,我有以下代表:
public delegate IBar FooDelegate(string str);
public delegate IBar FooDelegateWithRef(string str, IBar someBar);
另一方面,我有一个泛型类:
public class MyBaseClass
where T : IBar, new()
{
public FooDelegate myFunc;
public FooDelegateWithRef myFuncWithRef;
}
public class MyClass<T> : MyBaseClass
where T : IBar, new()
{
public MyClass()
{
myFunc = Foo; //"Wrong return type"
myFuncWithRef = FooWithRef; //"No overload...matches delegate"
}
public T Foo(string){ ... }
public T FooWithRef(string, IBar){ ... }
}
我的问题是当我执行以下操作时:
FooDelegate fooRef = MyClassInstance.Foo;
我收到"错误的返回类型"错误。我知道委托签名必须与方法签名匹配,但是既然泛型中的"where"指令实际上明确指定了 T 是 IBar,为什么它不起作用?
所以两个问题合二为一: - 为什么编译器拒绝考虑方法签名是否匹配? - 更重要的是,我怎样才能做到这一点?我更喜欢委托友好的解决方案,而不是出于约定原因使用 Func。
注意:我试图四处寻找答案,但我可能对这个问题的措辞有错误的措辞,所以如果之前已经回答过这个问题,请随时打我的脸。
编辑:正如@Jonathon Chase指出的那样,我的示例代码并没有完全包装问题。可以在此处找到一个非工作示例。编辑了上面的代码以反映问题。
编辑2:所有答案对我来说非常有用,非常感谢您的时间。如果可以的话,我会检查所有三个!
"错误的返回类型"错误是因为方差不支持值类型。 因此,实现IBar
的class
可以转换,而实现struct
则不会:
class RefTypeBar : IBar {}
struct ValueTypeBar : IBar {}
FooDelegate f1 = new MyClass<RefTypeBar>().Foo; // This works
FooDelegate f2 = new MyClass<ValueTypeBar().Foo; // Fails - wrong return type
错误是在MyClass<T>
内部生成的,因为T
可能是struct
,因此编译器不能保证该Foo
可以分配FooDelegate
。 如果将class
约束添加到MyClass<T>
则代码将编译。
public class MyClass<T> : MyBaseClass where T : class, IBar, new()
一定还有其他事情。我目前能够编译并运行以下示例并收到预期的结果:
public class Program
{
public static void Main()
{
var x = new MyClass<Bar>();
FooDelegate test = x.Foo;
test("Do It");
}
public delegate IBar FooDelegate(string str);
public interface IBar { }
public class Bar : IBar { }
public class MyClass<T> where T : IBar, new()
{
T item;
public T Foo(string input) { Console.WriteLine(input); return item; }
}
}
点网小提琴
在您的 DotNetFiddle 示例中,您可以通过将泛型约束更改为 where T: Item, new()
来使第一个分配给funcA
成为可能。
在第二个赋值中,委托将类型 T 用作返回类型和参数类型。我相信这会导致协方差和逆变的奇怪效应(MSDN 关于协变/逆变):让我们假设一个泛型实例使用一个类型class SubItem : Item {...}
对于类TypedFactory<T>
的类型参数T
。
可以使用SubItem
作为返回类型,因为返回类型仍将是(子)类型 Item
和委托变量(例如 funcA
) 仍然"满足"委托类型声明所描述的协定。
但是,如果我们使用 SubItem
作为参数类型会发生什么?委托变量(例如 funcB
)不能再在委托类型的声明所承诺的每个上下文中调用,例如 Item blubb; factory.funcB("I am alive too", blubb)
是不可能的 - 类型不匹配,因为blubb
不是类型 SubItem
.由于可能会发生这种情况,编译器必须在此处抱怨。
也许您可以选择使代表通用?
using System;
public interface IItem
{
string id {get;set;}
}
public class Item : IItem
{
public string id{get;set;}
}
public class BaseFactory<T>
where T: IItem, new()
{
public DelegateHolder.MakeWithID<T> funcA;
public DelegateHolder.MakeWithIDAndOther<T> funcB;
}
public class TypedFactory<T> : BaseFactory<T>
where T : IItem, new()
{
public TypedFactory()
{
funcA = makeNew;
funcB = makeNewFromOther;
}
public T makeNew(string itemId)
{
T _item = new T();
_item.id = itemId;
return _item;
}
public T makeNewFromOther(string itemId, T other)
{
T _item = new T();
_item.id = itemId;
return _item;
}
}
public class DelegateHolder
{
public delegate T MakeWithID<T>(string id) where T: IItem, new();
public delegate T MakeWithIDAndOther<T>(string id, T other) where T: IItem, new();
}
public class Program
{
public static void Main()
{
var x = new TypedFactory<Item>();
BaseFactory<Item> factory = x;
Item someItem = factory.funcA("I am alive");
Console.WriteLine(someItem.id);
Console.WriteLine(factory.funcB("I am alive too", someItem).id);
}
}