C#泛型类型

5

我在我的库中使用三个类:

public abstract class Base<TFirst, TSecond>
{
    public Base()
    {
      // actions with ID and Data of TFirst and TSecond
    }
}

public abstract class First<TFirstID, TFirstData>
{
    public TFirstID ID {get; set;}
    public TFirstData Data {get; set;}
}

public abstract class Second<TSecondID, TSecondData>
{
    public TSecondID ID {get; set;}
    public TSecondData Data {get; set;}
}

如何指定TFirst必须从First继承,TSecond必须从Second继承,而不使用Base中的泛型类型ID和Data?

像这样:

public abstract class Base<TFirst, TSecond>
    where TFirst : First // without generic-types
...

编辑: 在 First 和 Second 类中,我使用 TFirstID 和 TSecondID 作为属性。在 Base 类中,我使用这些属性。


请问您能否稍微澄清一下问题?首先:Base是指什么? - foson
你的意思是你不想要where TFirst : First<int,int>吗?我认为你不能这样做,我想Haskell允许类似这样的操作,但不确定。 - gideon
Foson:不,First没有继承Base。 - Aleksandr Vishnyakov
5个回答

6

除了引入一个没有泛型的并进行一些运行时检查的平行类层次结构,你无法以其他方式完成此操作:

public abstract class Base<TFirst, TSecond>
    where TFirst : First
{
    static Base()
    {
        if(!typeof(TFirst).IsGenericType || 
            typeof(TFirst).GetGenericTypeDefinition() != typeof(First<,>))
            throw new ArgumentException("TFirst");
    }
}

public abstract class First { }
public abstract class First<TFirstID, TFirstData> : First
{
}

或者,您可以使用标记接口(IFirst)替换First

运行时检查是可能的,因为静态构造函数会为每个封闭泛型类型调用。


非常有趣!但是他不能真正限制通用的 TFirstFirst<anytype,anytype>,它仍然可以是任何继承自 abstract First 的东西。我认为这可能是一种反模式,不是吗? - gideon

2
通常在这种情况下,我会建立另一个基类(非泛型),供First<TFirstID, TFirstData>继承,因此:
public abstract class First{}

public abstract class First<TFirstID, TFirstData>
    : First
{
}

然后您可以在声明中添加where TFirst : First。虽然不是完美的解决方案,但只要小心使用,它就能起到作用。但根据您尝试实现的目标,这可能会有些棘手 - 您将失去受限类型的所有通用性。


2

一个解决方案是让First和Second实现一个不依赖于泛型类型参数的接口:

public interface IFirst 
{
}

public abstract class First<TFirstID, TFirstData> : IFirst
{
}

然后确保基类中的类型参数必须使用IFirst

public abstract class Base<TFirst, TSecond>
   where TFirst : IFirst

这是避免使用实际类但允许将通用类型用作除Object之外的其他内容的唯一方法。 - KeithS

1

如果它们依赖于那些项的签名,那可能会很棘手。我可能会建议创建一个没有类型签名的接口或抽象基类。更可能是接口。


0

如果可能的话,这就是你应该这样做的方式:指定通用类型约束,使编译器能够捕获Base的通用参数的无效使用。

如果由于某种原因你不能使用通用类型约束,唯一强制进行类型检查的另一种方法是在逻辑中添加运行时检查,如果创建的通用类型指定了无效的通用类型,则抛出异常:

public abstract class Base<TFirst, TSecond>
{
    public Base()
    {
        if(!typeof(TFirst).IsAssignableFrom(typeof(First))
            throw new InvalidOperationException("TFirst must derive from First.");
        if(!typeof(TSecond).IsAssignableFrom(typeof(Second))
            throw new InvalidOperationException("TSecond must derive from Second.");
    }
}

上面的代码是一个严重的代码异味。泛型的整个目的是允许一个类与许多不同的内部类一起工作,同时允许编译器确保使用的参数类型是这样的,以便泛型类可以与它们一起工作。此外,你仍然必须能够引用 First 和 Second 的命名空间(我假设这就是你不能在第一次使用它们作为泛型类型参数的原因)。

我认为你的代码应该长成这个样子:typeof(TFirst).IsAssignableFrom(typeof(First<,>)) - Daniel Hilgarth
可能是这样,但我的观点是整个事情都是代码异味;通用类型限制应该以几乎任何代价实现,以避免出现这种情况。 - KeithS
当然,我同意你的观点。但是如果你发布代码,请确保它可以编译通过。否则,最好不要一开始就发布它。 - Daniel Hilgarth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接