条件接口

Cli*_*ton 6 c#

对于我.NET 2.0正在进行的项目,我很遗憾(我们的许多目标机器仍然是Windows XP),这意味着Nuget的 不需要任何Optional类型.Optional.NET 3.5

幸运的是,滚动自己的Optional类型非常简单,但我遇到了一个问题.

我想要以下内容:

class Optional<T> : (IComparable<Optional<T>> when T : IComparable<T>)
Run Code Online (Sandbox Code Playgroud)

也就是说,我希望我的Optional类型能够实现Comparable,但只有当底层类型是Comparable.

上面的语法遗憾地无效,但是有没有办法实现我正在寻找的东西?

实际上这个问题并不局限于Optional它,它将适用于任何想要定义的容器类型,它可以实现它的内部类型的接口.

我意识到我可以做到这一点:

class Optional<T>
class ComparableOptional<T> : Optional<T>, Comparable<ComparableOptional<T>> 
  where T : Comparable<T>
Run Code Online (Sandbox Code Playgroud)

但这似乎有点傻,因为那时我们真的要走下这个兔子洞:

class Optional<T>

class EquatableOptional<T> : 
    Optional<T>, 
    IEquatable<EquatableOptional<T>>
  where T : IEquatable<T>

class ComparableOptional<T> : 
    EquatableOptional<T>,
    IComparable<ComparableOptional<T>>, 
    IEquatable<ComparableOptional<T>>
  where T : IComparable<T>
Run Code Online (Sandbox Code Playgroud)

此外,如果TEnumerable,Optional<T>也可以Enumerable(如果没有值则返回一个空的枚举器),那么我们就有了更多的类.

由于Enumerable是垂直于EquatableComparable,我们就真的需要以下类:

class Optional
class EquatableOptional
class ComparableOptional
class EnumerableOptional
class EnumerableEquatableOptional
class EnumerableComparableOptional
Run Code Online (Sandbox Code Playgroud)

涵盖所有案件.添加另一个正交接口,你有12个类.

是否有一个不那么混乱的方法,允许我有条件地定义接口?这似乎是任何集合的常见问题.

Fla*_*ter 3

您想要做的事情违反了泛型在 C# 中工作的意图。

您本质上是在争论类型安全应该用作类型可能性。这违背了当前的 C# 意识形态,即您知道类型的定义(以及它公开的方法和属性)。

正确的方法是使用第二个方法ComparableOptional<T>,它派生自Optional<T>但添加了额外的约束:

class ComparableOptional<T> : Optional<T> where T : Comparable<T>
Run Code Online (Sandbox Code Playgroud)

除了想要将两个不同的类混在一起的懒惰方法之外,您的建议没有任何好处。即使该语言允许您这样做,我也认为这种方法没有明显的好处(与 相比ComparableOptional<T>),但它确实引入了您现在可能遇到的一系列运行时错误。


class Optional<T> : (IComparable<Optional<T>> when T : IComparable<T>) {}
Run Code Online (Sandbox Code Playgroud)

假设一切都按照您期望的方式进行。

var optionalPerson = new Optional<Person>() { Person = myPerson };
var optionalPerson2 = new Optional<Person>() { Person = myPerson2 };

int result = optionalPerson.CompareTo(optionalPerson2);
Run Code Online (Sandbox Code Playgroud)

这应该有效吗?目前在 C# 中,还没有。但根据你的说法,应该可以 if Person : IComparable<Person>。你的论点应该是这样的:

由于编译器看到我使用 type Person : IComparable<Person>,它应该能够推断出现Optional<T>在必须实现IComparable<T>,因此CompareTo()应该可用。

您的论点的可靠性仅取决于以下事实:您知道(在编译时)您正在使用的类型实现了所需的接口。

但是这段代码怎么样:

public void DoSomething<T>(Optional<T> opt1, Optional<T> opt2)
{
    int result = opt1.CompareTo(opt2);
}
Run Code Online (Sandbox Code Playgroud)

这应该有效吗?你无法知道,因为你不知道将使用哪种类型!使问题进一步复杂化:

public void DoSomething(string optionalType, object opt1, object opt2)
{
    var castObj = Convert.ChangeType(opt1, Type.GetType(optionalType)));
    var castObj2 = Convert.ChangeType(opt2, Type.GetType(optionalType)));

    int result = castObj .CompareTo(castObj2);
}
Run Code Online (Sandbox Code Playgroud)

此方法将使用的类型作为字符串传递。因此,现在您希望编译器检查字符串的值,以确定字符串中表示的类型的泛型类型约束是否实现特定的接口。

如果从数据库或外部 Web 服务检索该字符串会怎么样?编译器现在是否需要具有活动的数据库/Web 连接才能确定您的代码是否有效?

这已经失控了。

您可能的反驳:

只要我只将此方法与实现的类型一起使用IComparable<T>,编译器就不会抛出错误。当我使用未实现的类型时IComparable<T>,它应该会抛出错误int result

这不直观,并且会导致开发人员感到困惑。

编译器应始终假设条件泛型类型约束为 true

那么,您将如何处理互斥的条件泛型类型约束,从逻辑上讲,它们永远不会同时成立?

欢迎来到调试地狱的世界。这是一种不好的做法,其原因与您不应该使用dynamic强类型方法的原因相同:它使代码相当难以维护和开发。

这种方法需要更多的运行时测试,以确保您没有在某个地方犯下会在您面前爆炸的错误。运行时测试是一种有缺陷的方法。