我可以做一个通用的可选项,默认为某个类吗? [英] Can I make a generic optional, defaulting to a certain class?

查看:161
本文介绍了我可以做一个通用的可选项,默认为某个类吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题涉及到是否有合理的方法来默认类型参数在C#泛型?中,但使用内部泛型类的方法不起作用。



给定的代码如下所示:

  using System; 

公共类FooEventArgs< T> :EventArgs
{
// ... T属性和构造函数
}

public class Foo< T>
{
public delegate void EventHandler< FooEventArgs>(object sender,FooEventArgs< T> e);
公共事件EventHandler< FooEventArgs< T>>改变
}

并且像这样使用它:

  public class User 
{
public Foo< int> foo1;
public Foo< object> foo2的;
$ b $ 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):错误CS0123:没有为'foo3_Changed'重载匹配委托'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>)':不允许用户自定义的转换到基类或从基类转换



那么,再次,我有什么可以做的,或者我认为它是艰难的运气是正确的,我只需要使用 FooEventArgs< object> (然后我想我也可以使用 Foo< object> )?

解决方案

老实说,我不认为你可以对此做很多事情。您可 制作 Foo 双重通用:

  public class Foo< TData,TArgs>其中TArgs:FooEventArgs< TData> 
{
public delegate void EventHandler< TArgs>(object sender,TArgs e);
公共事件EventHandler< TArgs>改变;
}

然后你可以这样写:

  public class Foo:Foo< object,FooEventArgs> 

...但它确实让事情变得非常复杂,收益甚微。



我也会说,尽管包含类型参数有点冗长,但它确实说得很清楚 - 而继承可以以各种方式混淆水域。当你不是真的试图模拟行为特化时,我会避开类继承。



隐式转换的原因不是工作与泛型没有任何关系,正如错误信息所述,您不能声明在继承层次结构中向上或向下的转换(隐式或显式)。从C#规范第6.4.1节:


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


(详情请参阅本节。)




作为一个方面说明,我发现使用继承其他方式遍历泛型,通常使用接口更为常见:

  public interface IFoo 
{
//不依赖于类型参数的成员
}

公共接口IFoo< T> :IFoo
{
//所有使用T
}的会员

这样,如果代码不需要知道 T ,那么代码可以只接收 IFoo ,而不必担心泛型的一面。 $ c $。

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


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.

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 object would be the only real answer for that case.)

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_Changed accepting FooEventArgs; it needs FooEventArgs<object>, as that's what the Foo.Changed event will get pass to it (as the value will come from 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.

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

解决方案

I don't think there's much you can do about it, to be honest. You could make Foo doubly generic:

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 really trying 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# permits only certain user-defined conversions to be declared. In particular, it is not possible to redefine an already existing implicit or explicit conversion.

(See that section for more details.)


As a side note, I find it more common to use inheritance the other way 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 IFoo without worrying about the generics side of things if they don't need to know T.

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

这篇关于我可以做一个通用的可选项,默认为某个类吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆