代表"错误的退货类型"

时间:2016-01-23 23:58:23

标签: 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 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;

我得到了错误的回复类型&#39;错误。我知道委托签名必须匹配方法签名,但是因为&#34;其中&#34;通用中的指令实际上明确指出T IBar,为什么它不起作用?

所以两个问题合而为一:   - 为什么编译器拒绝考虑方法签名是否匹配?   - 更重要的是,我怎样才能做到这一点?我更喜欢一个委托友好的解决方案,而不是因为惯例而使用Func。

注意:我试着四处寻找答案,但我可能对这个问题有错误的措辞,所以请随意拍我的脸如果以前已经回答过。

编辑:正如@Jonathon Chase指出的那样,我的示例代码并没有完全解决问题。可以找到一个非工作示例here。编辑上面的代码以反映问题。

编辑2 :所有答案都非常适合我,非常感谢您的时间。如果可以的话,我会检查所有三个!

3 个答案:

答案 0 :(得分:4)

&#34;错误的返回类型&#34;错误是因为variance不是support value types。因此,class实施IBar可以转换,而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分配{ {1}}。如果向FooDelegate添加class约束,则代码将编译。

MyClass<T>

答案 1 :(得分:3)

这里的例子肯定会有其他事情发生。我目前能够编译并运行以下示例并获得预期结果:

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

答案 2 :(得分:2)

至少在您的DotNetFiddle示例中,您可以通过将通用约束更改为funcA来对where T: Item, new()进行第一次分配。

在第二个赋值中,委托使用类型T作为返回类型和参数类型。我相信这会导致协方差和逆变的有时奇怪的影响(getaddrinfo()): 我们假设一个通用实例使用类型class SubItem : Item {...} 对于您的班级T的类型参数TypedFactory<T>

可以使用SubItem作为返回类型,因为返回类型仍然是(子)类型Item,而委托变量(例如funcA)仍然是&# 34;满足&#34;委托类型声明所描述的合同。

但如果我们将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);
    }
}