C#泛型和泛型约束

3
我正在编写一个带有类型参数T的通用抽象类A,我打算用类B派生它。
A有一个数据成员mX,它是类C的实例,并具有一个泛型函数。这个泛型函数GetAllOfType()有一个类型参数T,该类型参数被限制为第三个类D。GetAllOfType()搜索一组包括了D的派生类实例的D实例容器,返回其中属于类型T的子集合(因此对D进行了约束)。
D类本身没有特定的数据成员int mY,但是一些派生类D(如E和F)有。
public class D
{
}

public class E : D
{
    public int mY;
}

public class F : D
{
    public int mY;
}

public class C
{
    public T[] GetAllOfType<T>() where T : D
    { ... }
}

public class A<T>
{
    private C mX;
    ...
}

public class B : A<E>
{
    ...
}

以下是我的问题:

B类继承并实现A类时使用的参数类型是D的派生类。我试图在A类中编写一个函数Foo,该函数通过GetAllOfType()枚举并访问类型为E、F或任何具有mY成员的其他成员的mY成员。

public class A<T>
{
    private C mX;

    protected Foo()
    {
        foreach (var c in mX.GetAllOfType<T>())
        {
            c.mY = 0;
        }
    }
}

public class B : A<E>
{
    public Bar()
    {
        Foo();
    }
}

问题在于 GetAllOfType() 受限,并且由于未对 A 类进行限制而导致错误。
我尝试将 A 进行如下约束:
public class A<T> where T : D

但是我遇到了编译时错误,大致如下:

类型 T 不包含定义为 mY 的内容,并且找不到类型 T 的扩展方法 mY(是否缺少 using 指令或程序集引用?)

我还尝试了对多个派生类进行约束:

public class A<T> where T : E, F

但我也遇到了一个类似的错误:
类类型约束“F”必须列在任何其他约束之前。考虑将类型约束移动到约束列表的开头。
我尝试过交换它们:
public class A<T> where T : F, E

同样,结果是相同的错误,但将E替换为F。
我所尝试的是否可能?我做错了什么?
同样,我不能更改类C、D、E和F中的任何内容。

你尝试过使用 foreach (T c in mX.GetAllOfType<T>()) 吗?在这种情况下,你需要将成员变量 mY 移动到你的 D 基类中。对于 public class A <T> where T:E,F,代码期望 T 实现了 EF,如果我没有弄错的话。 - John Kalberer
为什么不将 int mY 添加到 class D 中,因为所有派生类都有它? - John Alexiou
1个回答

2

在对A的泛型参数添加约束时,

public class A<T> where T : D

你遇到的第二个错误信息是:Type T' does not contain a definition for mY' and no extension method mY' of typeT' could be found (are you missing a using directive or an assembly reference?),原因并不意外,因为类型 D 并没有定义 mY,只有派生类 E 和 F 中才有。这就是错误发生的原因。

public class A<T> where T : E, F

由于C#不支持多重继承,所以无法指定多个基类类型约束,这就是为什么它不起作用的原因。任何进一步的约束都必须是接口,或者像classnew()这样的东西。
如果你真的不能改变这些类的任何内容,那么你想要实现的目标可能是不合理的难以实现,据我所知。我可以想到一些丑陋的方法(反射或大量的转换),但这并不是正确的方法。也许有些聪明的东西我没想到。如果您能引入一个带有mY属性的接口(或中间类),并使E和F从中继承,那么您就可以将T约束为该接口,而不是D,一切都会很好。否则,除了在运行时之外,我看不到任何了解你正在处理的D类型的方法。

1
反射是确定没有使用某种形式的继承定义属性的“正确”方法。它可能不太干净,但它能胜任工作。 - Guvante
@Guvante 是的,也许称其为丑陋并不公平。我希望能找到一种在不使用运行时检查的情况下解决问题的方法。 - Bort
没有运行时检查意味着基本上使用一个新的(真实的,拥有属性mY)接口。如果您需要与所有派生自D且还具有属性mY的对象进行交互,则需要引入E和F的共同祖先来公开mY。它可以是一个抽象类(例如LocalD)或一个接口(IHavemY)。 - Tetsujin no Oni

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