为泛型类型设置约束,T继承自另一个泛型类型

时间:2017-08-30 13:23:52

标签: c# .net generics

我想创建一个泛型类,但该类型的where条件之一是,它必须扩展一个特定的类,不幸的是它也是泛型类。

让我解释一下,说我们已经有了这个抽象基类(并且不能改变它们,因为它是第三方框架的一部分):

public abstract class SomeGenericBaseClass<Tx> : IDisposable
    where Tx : class
{
    public string SomeProperty { get; set; }
}

这个基类有几个后代,用具体类型实现它,如下所示:

public class SomeConcreteClass : SomeGenericBaseClass<string>
{}

我想创建一个类,它可以包装SomeGenericBaseClass的后代:

public class MyClass<Ta> : IDisposable
    // some magig where condition, that makes sure, Ta is a descendent of  SomeGenericBaseClass
{
    private Ta wrappedObject;

    public MyClass(Ta objectToWrap)
    {
        this.wrappedObject = objectToWrap;
    }

    public DoSomething()
    {
        this.wrappedObject.SomeProperty = "If I want to use this property, I have to make sure, that Ta/wrappedObject is a descendant of SomeGenericBaseClass, which declares SomeProperty.";
    }
}

如何在没有where条件的情况下执行此操作

public class MyClass<Ta, Tb> : IDisposable
    where Ta : SomeGenericBaseClass<Tb>, new()
    where Tb : class

这意味着每次使用MyClass

时我都必须传递两种类型
var obj = new MyClass<SomeConcreteClass, string>();

虽然Ta = SomeConcreteClass只允许一个有效的Tb类型(string)......

1 个答案:

答案 0 :(得分:1)

第二次更新

在您更新的问题中,您为我们提供了更具体的用法,Ta = SomeConcreteClassTb = string

这是一个更新的代码段,用于说明如何使用我的答案:

public class SomeGenericBaseClass<Tx> : IDisposable
    where Tx : class { }

public class MyClass<Ta> : IDisposable
    where Ta : SomeGenericBaseClass<string>, new()

var obj = new MyClass<SomeConcreteClass>();

如果这仍然不是你想要的,那么你就没有足够清楚地解释它(除非其他人设法回答它。我们正在谈论一个非常错综复杂的设置,这意味着很多< / strong>这里的可能性,但它们的有效性取决于您的具体要求。

我的最后一个示例假设每个 MyClass<Ta>对象都有一个Ta SomeGenericBaseClass<string>

但是,如果MyClass<ASecondConcreteClass>SomeGenericBaseClass<int>并且根据您的要求是正确的,那么我再次参考第3点:如果Tb特定于Ta { {1}}正在被使用,那么你显然必须同时提及它们(因为Tb属于哪个Ta并非含糊不清。

根据您的更新

更新
  

哎呀,我在输入SO时输入了一个拼写错误。 Tx / Tb应该只有一个类约束。我编辑了这个问题。

public class SomeGenericBaseClass<Tx> : IDisposable
    where Tx : class { }

public class MyClass<Ta> : IDisposable
    where Ta : SomeGenericBaseClass<Tb>, new()

如果这是正确的情况,那么就没有理由提及where Tb : class,因为已经定义了where Tx : class

,因此已经存在这种情况

因此,您可以简单地省略要求,因为它是多余的:

public class MyClass<Ta, Tb> : IDisposable
    where Ta : SomeGenericBaseClass<Tb>, new()

编译器本质上需要Tb来满足where Tx : class的要求。

旧答案

您的设置在逻辑上不一致。

首先,你设置你的类:

public class SomeGenericBaseClass<Tx> : IDisposable
    where Tx : class { }

此处SomeGenericBaseClass<Tx>IDisposable,但Tx只不过是一个类。

但是稍后你会发现一些不同的东西:

public class MyClass<Ta> : IDisposable
    where Ta : SomeGenericBaseClass<Tb>, new()
    where Tb : IDisposable

但是在这里,您希望Tb(前面示例中的Tx本身IDisposable

您的要求发生了变化。而且我不确定这是出于错误还是出于意图。我在这里看到三个选项:

1。 Tx / Tb始终需要IDisposable

这似乎是最有意义的,基于我对你想要达到的目标的假设。

public class SomeGenericBaseClass<Tx>
    where Tx : class, IDisposable { }

public class MyClass<Ta> : IDisposable
    where Ta : SomeGenericBaseClass<Tb>, new()

这放弃了MyClass<Ta>需要Tb所需的任何内容(当然只需要提及类型本身),因为您的要求已经是SomeGenericBaseClass<Tx>定义的一部分。

注意:您也可以SomeGenericBaseClass<Tx> IDisposable。这没有错;但我从我的例子中省略了它,以证明它不是必要

2。 Tx / Tb永远不需要IDisposable

如果是这种情况,那么您可能会在TxSomeGenericBaseClass<Tx>之间感到困惑,但事实上只有后者需要IDisposable

public class SomeGenericBaseClass<Tx> : IDisposable
    where Tx : class { }

public class MyClass<Ta> : IDisposable
    where Ta : SomeGenericBaseClass<Tb>, new()

3。 Tb有时需要IDisposableIDisposable应为MyClass<Ta>,但SomeGenericBaseClass<Tb/Tx>

的基本实现不应该Tb

请注意,在两个之前的案例中,我在MyClass<Ta>的类定义中省略了SomeGenericBaseClass<Tx>的任何类型要求。这是因为在这两种情况下,类型要求都是全球性的。然后,MyClass<Ta>的每个实例在整个应用程序中的行为方式都相同。

您想要为Tb添加这一要求并非不可能。

但如果这是您想要的,那么您无法避免在MyClass<Ta>定义中提及MyClass<Ta>。如果类型要求仅适用于public class SomeGenericBaseClass<Tx> : IDisposable where Tx : class { } public class MyClass<Ta, Tb> : IDisposable where Ta : SomeGenericBaseClass<Tb>, new() where Tb : IDisposable 的范围,那么您必须将其置于此处(并且必须提及该类型是明显的后果)。

SomeGenericBaseClass<Tx>

请注意,在这种情况下,IDisposable 始终必须为Tx,但IDisposable本身必须为MyClass<A> 仅适用于{{1}}

但是,我认为这种设计虽然可能,但并不是一种好方法。至少,这是我的意见,因为我无法想到这个设置的有效用例。我有兴趣听听你为什么要这样使用它,如果是这样的话。