【问题标题】:Why does IParseable<T> need to be recursive?为什么 IParseable<T> 需要递归?
【发布时间】:2022-11-21 06:39:22
【问题描述】:

为什么IParseable&lt;T&gt;T : IParseable&lt;T&gt; 约束放在T 上?需要这个递归约束是为了什么?

【问题讨论】:

  • 也就是说,如果我的 T 实现了IParseable&lt;T&gt;,那么我的 T 必须实现IParseable&lt;T&gt;
  • 请不要将代码粘贴为图像。始终粘贴为文本。

标签: c# recursion interface c#-11.0


【解决方案1】:

这就是所谓的Curiously_recurring_template_pattern(CRTP),据我所知,它并不是严格要求的(实际上它不能强制执行“正确”行为)但正如Preview Features in .NET 6 – Generic Math文章中提到的,它的用法是一个提示支持静态抽象接口成员调用的一个非常重要的场景——通过通用接口使用它:

这种通用模式有时称为奇怪的重复模板模式 (CRTP),是使该功能正常工作的关键。

让我们想象一下以下界面:

public interface IParseable1<TSelf>
    // where TSelf : IParseable1<TSelf>
{
    static abstract TSelf Parse(string s, IFormatProvider? provider);

    static abstract bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out TSelf result);
}

和下一个方法:

static T InvariantParse<T>(string s)
    where T : IParseable1<T>
{
    return T.Parse(s, CultureInfo.InvariantCulture);
}

如果我们实现下一个类对:

class MyClass1
{
}

class MyClass : IParseable1<MyClass1>
{
    public static MyClass1 Parse(string s, IFormatProvider? provider)
    {
        throw new NotImplementedException();
    }

    public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out MyClass1 result)
    {
        throw new NotImplementedException();
    }
}

然后下一个调用将不会编译:

var x = InvariantParse<MyClass>("");

虽然 CRTP 不会阻止“不正确”的使用(即 class MyClass1 : IParsable&lt;MyClass1&gt; 将允许 class MyClass : IParsable&lt;MyClass1&gt;,据我所知,没有语言结构来强制执行此类行为),但它在很大程度上暗示了所需的用法。

请注意,class MyClass : IParsable&lt;MyClass1&gt; 可以用在类似的方法中,但它变得相当笨拙(部分原因是 C# 泛型类型推断的细节):

public static TOut InvariantParseTwoGenerics<T, TOut>(string s)
    where T : IParseable1<TTO>
{
    return T.Parse(s, CultureInfo.InvariantCulture);
}

var x1 = InvariantParseTwoGenerics<MyClass, MyClass1>("");

【讨论】:

  • 是的我同意。 TSelf 暗示很重。
猜你喜欢
  • 2018-07-13
  • 1970-01-01
  • 2018-05-31
  • 2021-01-02
  • 2021-02-23
  • 2014-11-17
  • 2015-01-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多