C# “代表”;“返回类型错误”;

C# “代表”;“返回类型错误”;,c#,delegates,C#,Delegates,一方面,我有以下代表: 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 FooDelegateWith

一方面,我有以下代表:

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){ ... }
}
我得到一个“错误的返回类型”错误。我知道委托签名必须与方法签名匹配,但既然泛型中的“where”指令实际上明确指定T是IBar,为什么它不起作用

因此,两个问题合一: -为什么编译器拒绝考虑方法签名匹配? -更重要的是,我如何才能使这项工作?出于惯例的原因,我更喜欢代表友好的解决方案,而不是使用Func

注意:我试着四处寻找答案,但我可能对这个问题有错误的措辞,所以如果以前有人回答过,请随意打我一耳光

编辑:正如@Jonathon Chase指出的,我的示例代码并没有完全解决这个问题。可以找到一个不起作用的例子。编辑上述代码以反映问题


编辑2:所有答案对我来说都非常有用,非常感谢您抽出时间。如果可以的话,我会把这三个都检查一遍

您的示例中肯定还有其他原因。我目前能够编译并运行以下示例,并收到预期结果:

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; }
    }
}
公共类程序
{
公共静态void Main()
{
var x=新的MyClass();
Foo委托测试=x.Foo;
测试(“做它”);
}
公共委托IBar FOODERegate(字符串str);
公共接口IBar{}
公共类栏:IBar{}
公共类MyClass,其中T:IBar,new()
{
T项;
公共T Foo(字符串输入){Console.WriteLine(输入);返回项;}
}
}

至少在您的DotNetFiddle示例中,您可以通过将通用约束更改为
,其中T:Item,new()
来实现对
funcA
的第一次赋值

在第二个赋值中,委托使用类型T作为返回类型和参数类型。我相信这会导致协方差和反方差有时会产生奇怪的效果(): 让我们假设泛型实例使用类型
类子项:Item{…}
对于类
TypedFactory
的类型参数
T

可以使用
子项
作为返回类型,因为返回类型仍然是(子)类型
,并且委托变量(例如
funcA
)仍然“满足”委托类型声明中描述的合同

但是如果我们使用
子项
作为参数类型,会发生什么呢?委托变量(例如
funcB
)不能再在委托类型声明承诺的每个上下文中调用,例如
Item blubb;factory.funcB(“我也活着”,blubb)
不可能-类型不匹配,因为
blubb
不是
子项的类型。由于这种情况可能发生,编译器不得不在这里抱怨

也许您可以选择将代理设置为通用的

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);
    }
}
使用系统;
公共接口项
{
字符串id{get;set;}
}
公共类项目:IItem
{
公共字符串id{get;set;}
}
公共类基工厂
其中T:IItem,new()
{
public DelegateHolder.MakeWithID funcA;
public DelegateHolder.makewithid和其他functb;
}
公共类TypedFactory:BaseFactory
其中T:IItem,new()
{
公共类型工厂()
{
funcA=makeNew;
funcB=makenewfromther;
}
public T makeNew(字符串itemId)
{
T_项=新的T();
_item.id=itemId;
退货项目;
}
公共T makeNewFromOther(字符串itemId,T other)
{
T_项=新的T();
_item.id=itemId;
退货项目;
}
}
公共类委托人
{
公共委托T MakeWithID(字符串id),其中T:IItem,new();
公共委托T MakeWithIDAndOther(字符串id,T other),其中T:IItem,new();
}
公共课程
{
公共静态void Main()
{
var x=新类型工厂();
基本工厂=x;
Item someItem=factory.funcA(“我还活着”);
Console.WriteLine(someItem.id);
Console.WriteLine(factory.funcB(“我也活着”,someItem).id);
}
}
错误的返回类型是因为没有。因此可以转换
实现
IBar
,而
结构
实现不会:

class RefTypeBar : IBar {}
struct ValueTypeBar : IBar {}

FooDelegate f1 = new MyClass<RefTypeBar>().Foo;  // This works
FooDelegate f2 = new MyClass<ValueTypeBar().Foo; // Fails - wrong return type

你是对的,我认为我发布的简短版本会出现问题,但事实并非如此。显然,对于委托,我还没有意识到一些微妙之处,下面是我在DotNetFiddle上遇到的问题的一个例子:哦,在这种情况下,问题是试图将非泛型委托应用于泛型函数。如果您使用相同的约束使委托成为泛型的,则应该可以,或者尝试将其应用于已声明实例上的方法,就像我在上面的代码示例中所做的那样。但我不能使其成为泛型的,基类只能处理接口,而不知道其泛型子类:(我要么需要牺牲泛型实现的某些部分来使用接口而不是T,要么创建一个与委托签名匹配的伪函数,这将破坏其最初的目的。愚蠢的我认为,使用接口作为泛型约束也隐含着“类”——我不知道你可以使用接口带值类型的es。解决了第一个问题,添加类是一种方法。确实,我可以使用泛型委托,但在基类上,我只需要处理接口,所以我不能在这里真正使用这个技巧…:(非常有用--非常感谢!
class RefTypeBar : IBar {}
struct ValueTypeBar : IBar {}

FooDelegate f1 = new MyClass<RefTypeBar>().Foo;  // This works
FooDelegate f2 = new MyClass<ValueTypeBar().Foo; // Fails - wrong return type
public class MyClass<T> : MyBaseClass where T : class, IBar, new()