C# 我可以做一个通用的可选,默认为某个类吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13717802/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 09:33:01  来源:igfitidea点击:

Can I make a generic optional, defaulting to a certain class?

c#generics.net-3.5

提问by Chris Morgan

My question is related to Is there a reasonable approach to "default" type parameters in C# Generics?, but using an inner generic class that approach doesn't work.

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

Given code like this:

给定这样的代码:

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
}

And with it being used like this:

并且像这样使用它:

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) { ... }
}

Well, I'd rather like it if I could have the generic optional, as there will be many cases where I don't know what type something will be coming in. (Data is coming from an external system which has its own variable types, which are then converted into .NET types, but I run into situations where, for example, one remote data type may turn into one of a couple of .NET types, or where it is of the "any" type—thus objectwould be the only real answer for that case.)

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

The solution which immediately occurred to me was subclassing (it was also the primary suggestion in the question linked to earlier):

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

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

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

I then want to use it like this:

然后我想像这样使用它:

public class User
{
    public Foo foo3;

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

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

The problem is that it naturally won't work with foo3_Changedaccepting FooEventArgs; it needs FooEventArgs<object>, as that's what the Foo.Changedevent will get pass to it (as the value will come from Foo<object>).

问题是它自然不会与foo3_Changedaccept 一起工作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>>'

Is there anything I can do about this, short of duplicating much of the class?

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

I did try one other thing: an implicit operator to convert from FooEventArgs<object>to FooEventArgs.

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

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

This, unfortunately, doesn't seem to work, though I'm not quite clear on why:

不幸的是,这似乎不起作用,尽管我不太清楚原因:

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

So then, once again, is there anything I can do about this, or am I correct in thinking that it's Tough Luck and I'll just have to be content using FooEventArgs<object>(and then I guess I may as well just use Foo<object>)?

那么,再一次,我对此有什么可以做的,还是我认为这是艰难的运气,我只需要满足于使用FooEventArgs<object>(然后我想我也可以只使用Foo<object>)?

采纳答案by Jon Skeet

I don't think there's much you can do about it, to be honest. You couldmake Foodoubly generic:

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

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

Then you could write:

然后你可以写:

public class Foo : Foo<object, FooEventArgs>

... but it's really making things very complicated for very little benefit.

......但它确实让事情变得非常复杂,而且收益很小。

I would also say that even though it's a bit more verbose to include the type argument, it does make it very clear - whereas inheritance can muddy the waters in various ways. I'd steer clear of class inheritance when you're not reallytrying to model behaviour specialization.

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

The reason your implicit conversion doesn't work has nothing to do with generics, by the way - as the error message states, you can't declare a conversion (implicit or explicit) which goes up or down the inheritance hierarchy. From the C# spec section 6.4.1:

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

C# permits only certain user-defined conversions to be declared. In particular, it is not possible to redefine an already existing implicit or explicit conversion.

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

(See that section for more details.)

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



As a side note, I find it more common to use inheritance the otherway round for generics, typically with interfaces:

作为一个方面说明,我觉得它更常见的继承使用其他方式轮与泛型接口,通常为:

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

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

That way code can receive just an IFoowithout worrying about the generics side of things if they don't need to know T.

这样IFoo,如果不需要知道T.

Unfortunately, that doesn't help you in your specific case.

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

回答by Prime By Design

Another interesting thing I just found is that you can create generic classes with the same name but different signatures.

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

class Foo<T> { 

}

class Foo<T,T> { 

}

then you can call either one of them like follows:

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

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

I just thought it was interesting that despite both classes having the same name they can co-exist because they have different signatures.

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

I guess this is how the Tuple class works

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

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