【问题标题】:Can I make a generic optional, defaulting to a certain class?我可以制作一个通用的可选,默认为某个类吗?
【发布时间】:2012-11-22 22:59:58
【问题描述】:

我的问题与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&lt;object&gt;,因为这就是Foo.Changed 事件将传递给它的内容(因为值将来自Foo&lt;object&gt;)。

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

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

我确实尝试了另一件事:将隐式运算符从 FooEventArgs&lt;object&gt; 转换为 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&lt;object&gt;(然后我想我也可以)只需使用Foo&lt;object&gt;)?

【问题讨论】:

标签: c# generics .net-3.5


【解决方案1】:

老实说,我认为您对此无能为力。您可以使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# 只允许声明某些用户定义的转换。特别是,不可能重新定义已经存在的隐式或显式转换。

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


顺便说一句,我发现以other方式对泛型使用继承更为常见,通常使用接口:

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

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

这样代码可以只接收IFoo,而不必担心它们不需要知道T的泛型方面。

很遗憾,这对您的具体情况没有帮助。

【讨论】:

  • 这是关于处理接口继承的反向方式的有趣见解。但正如您所观察到的,在这种情况下不会这样做。谢谢。
  • @ChrisMorgan:是的——我只是把它作为一个附带问题提出来。我用另一个想法编辑了答案的开头,但这不是我真正喜欢的一个。
  • 不,我也不喜欢它......好吧,我认为它是明确的。
  • 乔恩一如既往的好答案。
【解决方案2】:

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

class Foo<T> { 

}

class Foo<T,T> { 

}

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

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

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

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

public class Tuple<T1, T2, T3... T8> 
{ 
...

【讨论】:

  • 好答案。只是类似于重载方法的工作原理
  • @mojoblanco 类似,在班级级别。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-22
  • 2017-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-17
相关资源
最近更新 更多