接口内静态字段的惯用用例是什么?

J. *_*ini 6 c# static design-patterns field interface

我在这个网站上搜索了任何标记为 内容,发现这个主题很少出现。其他网站上的例子也同样不令人满意,我自己还没有想出任何办法。因此,我发现很难想到包含静态字段的接口的用例。

是否有任何设计模式或任何其他习惯用法建议在接口内部使用静态字段?

Oli*_*bes 10

传统上,接口只是契约,不包含可执行代码或状态信息,而仅包含必须由类或结构实现的抽象实例成员。C# 8.0 和 C# 11 中引入的两个功能向接口添加静态成员。只有第一个将可执行代码添加到界面,并且可能需要保存状态:

  1. 在 C# 8.0 中,Microsoft 引入了默认接口方法。类可以选择实现这些方法。如果未实现它们,则会启动默认实现。但是,这些方法不是由类继承的,只能通过接口调用(即使在实现接口的类中)。

    主要动机是允许在不破坏现有代码的情况下扩展接口。

    静态字段和其他不属于公共接口的成员用于支持这些默认接口方法的实现。C# 语言对默认接口方法的建议如下:

接口可能不包含实例状态。虽然现在允许静态字段,但接口中不允许实例字段。接口中不支持实例自动属性,因为它们会隐式声明隐藏字段。

静态和私有方法允许对用于实现接口的公共 API 的代码进行有用的重构和组织。

  1. C# 11 还提供了另一个功能:接口中的静态虚拟成员。这些必须由实现类来实现。引入它们主要是为了允许为具有静态运算符方法(+、-、*、/ 等)的数字类型制定接口。但它们也允许声明静态工厂方法。这些接口在泛型类型约束中很有用。

这是一个完整的用例。新属性被添加到接口中。为了不破坏现有的实现,它们添加了默认实现。

由于我们无法在接口中存储实例状态(接口未实例化),因此我们无法使用属性的实例支持字段。相反,我们使用以对象引用作为键的字典来存储属性值。(我们也可以使用弱引用,但这超出了本文的范围。)字典存储在私有静态字段中。这与 WPF 存储依赖属性的方式相当。

interface IUseCase
{
    string Name { get; set; } // Public by default, must be implemented by the class

    // New property added in a later release with a default implementation.
    // Implementation by class is optional.
    private static Dictionary<IUseCase, string?> _firstNames =
        new(ReferenceEqualityComparer.Instance);
    string? FirstName
    {
        get {
            _firstNames.TryGetValue(this, out string? s);
            return s; // s is null when TryGetValue returns false 
        }
        set {
            _firstNames[this] = value;
        }
    }

    // New property with default implementation.
    string FullName => $"{FirstName} {Name}";
}
Run Code Online (Sandbox Code Playgroud)

在扩展接口之前创建的类仅实现第一个属性。

class UseCase : IUseCase
{
    public UseCase(string name)
    {
        Name = name;
    }

    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

测试:

class UseCase : IUseCase
{
    public UseCase(string name)
    {
        Name = name;
    }

    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

印刷:

IUseCase u1 = new UseCase("Doe");
u1.FirstName = "John";

IUseCase u2 = new UseCase("Poe");
u2.FirstName = "Jane";

Console.WriteLine(u1.FullName);
Console.WriteLine(u2.FullName);
Run Code Online (Sandbox Code Playgroud)