46

我的问题与C# 泛型中的“默认”类型参数是否有合理的方法有关?,但是使用该方法不起作用的内部泛型类。

给定这样的代码:

using System;

public class FooEventArgs<T> : EventArgs
{
    // ... T properties and a constructor
}

public class Foo<T>
{
    public delegate void EventHandler<FooEventArgs>(object sender, FooEventArgs<T> e);
    public event EventHandler<FooEventArgs<T>> Changed
}

并像这样使用它:

public class User
{
    public Foo<int> foo1;
    public Foo<object> foo2;

    public User()
    {
        foo1 = new Foo<int>();
        foo2 = new Foo<object>();
        foo1.Changed += foo1_Changed;
        foo2.Changed += foo2_Changed;
    }

    protected void foo1_Changed(object sender, FooEventArgs<int> e) { ... }
    protected void foo2_Changed(object sender, FooEventArgs<object> e) { ... }
}

好吧,如果我可以拥有通用可选,我会更喜欢它,因为在很多情况下我不知道会进入什么类型。(数据来自具有自己的变量类型的外部系统,然后将其转换为 .NET 类型,但我遇到的情况是,例如,一种远程数据类型可能会变成几种 .NET 类型中的一种,或者它是“任何”类型 - 因此object将是这种情况下唯一真正的答案。)

我立即想到的解决方案是子类化(这也是之前链接的问题中的主要建议):

public class Foo : Foo<object>
{
    public Foo(...) : base(...) { }
}

public class FooEventArgs : FooEventArgs<object>
{
    public Foo(...) : base(...) { }
}

然后我想像这样使用它:

public class User
{
    public Foo foo3;

    public User()
    {
        foo3 = new Foo();
        foo3.Changed += foo3_Changed;
    }

    protected void foo3_Changed(object sender, FooEventArgs e) { ... }
}

问题是它自然不适用于foo3_Changed接受FooEventArgs;它需要FooEventArgs<object>,因为这就是Foo.Changed事件将传递给它的内容(因为值将来自Foo<object>)。

Foo.cs(3,1415926): error CS0123: No overload for 'foo3_Changed' matches delegate 'FooLibrary.Foo<object>.EventHandler<FooLibrary.FooEventArgs<object>>'

除了复制大部分课程之外,我能做些什么吗?

我确实尝试了另一件事:一个隐式运算符从转换FooEventArgs<object>FooEventArgs.

    public static implicit operator FooEventArgs(FooEventArgs<object> e)
    {
        return new FooEventArgs(...);
    }

不幸的是,这似乎不起作用,尽管我不太清楚为什么:

EditBuffer.cs(13,37): error CS0553: 'FooLibrary.FooEventArgs.implicit operator FooLibrary.FooEventArgs(FooLibrary.FooEventArgs<object>)': user-defined conversions to or from a base class are not allowed

那么,再一次,我能做些什么呢,还是我认为这是运气不好,我只需要满足于使用FooEventArgs<object>(然后我想我也可以使用Foo<object>)是正确的吗?

4

2 回答 2

47

老实说,我认为您对此无能为力。你可以使Foo双重通用:

public class Foo<TData, TArgs> where TArgs : FooEventArgs<TData>
{
    public delegate void EventHandler<TArgs>(object sender, TArgs e);
    public event EventHandler<TArgs> Changed;
}

然后你可以写:

public class Foo : Foo<object, FooEventArgs>

...但它确实让事情变得非常复杂而没有什么好处。

我还要说,尽管包含 type 参数有点冗长,但它确实使它非常清楚 - 而继承会以各种方式混淆水域。当您不是真正尝试对行为专业化建模时,我会避开类继承。

顺便说一句,您的隐式转换不起作用的原因与泛型无关 - 正如错误消息所述,您不能声明在继承层次结构中上升或下降的转换(隐式或显式)。从 C# 规范第 6.4.1 节:

C# 只允许声明某些用户定义的转换。特别是,不可能重新定义已经存在的隐式或显式转换。

(有关详细信息,请参阅该部分。)


作为旁注,我发现对泛型使用相反的继承更为常见,通常使用接口

public interface IFoo
{
    // Members which don't depend on the type parameter
}

public interface IFoo<T> : IFoo
{
    // Members which all use T
}

这样IFoo,如果不需要知道,代码就可以只接收一个,而不用担心泛型方面的事情T

不幸的是,这对您的具体情况没有帮助。

于 2012-12-05T06:54:08.733 回答
39

我刚刚发现的另一个有趣的事情是,您可以创建具有相同名称但不同签名的泛型类。

class Foo<T> { 

}

class Foo<T,T> { 

}

那么你可以像下面这样调用它们中的任何一个:

new Foo<string>();
new Foo<int,double>();
new Foo<string,int>();

我只是觉得有趣的是,尽管两个类具有相同的名称,但它们可以共存,因为它们具有不同的签名。

我想这就是元组类的工作方式

public class Tuple<T1, T2, T3... T8> 
{ 
...
于 2017-03-12T23:01:29.157 回答