我可以制作一个通用的可选项,默认为某个类吗?

时间:2012-12-05 06:50:38

标签: c# generics .net-3.5

我的问题与Is there a reasonable approach to "default" type parameters in C# Generics?有关,但使用内部泛型类方法不起作用。

给出这样的代码:

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>)?

2 个答案:

答案 0 :(得分:30)

说实话,我认为你无能为力。你可以使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>

......但它确实让事情变得非常复杂而且收效甚微。

我还要说,即使包含类型参数更加冗长,但它确实非常清楚 - 而继承可以通过各种方式使水变得混乱。当你真的试图模拟行为专业化时,我会避开类继承。

隐式转换不起作用的原因与泛型无关,顺便说一句 - 正如错误消息所述,您无法声明在继承层次结构中上下移动的转换(隐式或显式)。来自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而不用担心泛型方面。

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

答案 1 :(得分:16)

我刚发现的另一个有趣的事情是你可以创建具有相同名称但签名不同的通用类。

class Foo<T> { 

}

class Foo<T,T> { 

}

然后你可以调用其中一个,如下所示:

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

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

我想这就是Tuple类的工作原理