【问题标题】:How to have C# interface with readonly member如何拥有只读成员的 C# 接口
【发布时间】:2020-05-28 00:06:00
【问题描述】:

界面不便

我最近发现自己需要一些东西,这在 C# 中应该很有可能(我知道它在 C++ 中):几个类需要一个 api 密钥,它绝对必须是一个私有的、不可变的字段(除了在构造函数中声明)。为了避免代码重复,我想为需要 api 密钥的类创建一个接口。

我会让代码自己说话:

public interface IHasApiKey
{
    protected readonly string _apiKey = String.Empty;
}

问题:

  • 我可以让它成为一个类,因为接口不能实例化成员属性。但是由于 C# 不允许多重继承,而且我认为这个特性是一种行为而不是一种实现,所以我不明白为什么它不应该是一个接口。而且它可能会与已经有基类的类发生冲突。
  • 我可以将其转换为属性,但无论可访问性级别如何,如果它是继承的,它仍然可以在派生类的方法中进行修改,而我真的想要@987654322的行为@。 (常量,但可以在构造函数中设置)
  • 我发现了属性System.ComponentModel.ReadOnlyAttribute,但是文档非常有限,而且它看起来不像readonly那样执行,而更像是一个可以在用户代码中查询的属性。
  • 如果我将其转换为自动属性,则所有派生类都需要指定要指向的私有数据成员,这再次意味着重复代码(我首先尝试通过使用此接口来避免这种情况)李>

为了完整起见,以下是我想象的 C++ 中正确代码的样子:

class IHasApiKey
{
private:
    std::string _apiKey = "";
protected:
    IHasApiKey(const std::string& apiKey) : _apiKey(apiKey) {}

    // tbo, I'm not quite sure about how to optimally write this one,
    // but the idea is the same: provide protected read access.
    const std::string& GetKey() { return const_cast<std::string&>(_apiKey); }
};

我解释得正确吗?有没有人知道如何优雅地解决这个问题? 提前非常感谢。

【问题讨论】:

  • “既然我认为这个特性是一种行为而不是一种实现,我不明白为什么它不应该是一个接口。”在 C# 中,接口是一个特定的东西,它只规定 clients 可以对实例做什么。它永远不能规定暴露接口的类应该如何实现它。从这个意义上说,“考虑”行为并不取决于您。这确实意味着您需要一个基类来强制执行这一点,如果一个类不喜欢从它继承,它就必须重新实现。如果不希望这样做,您可以使用封装和 sealed
  • "如果我将它转换为自动属性,那么所有派生类都需要指定一个私有数据成员指向,这再次意味着重复代码(我试图通过在这个接口中避免这种情况)第一名)” - 为什么不呢?您可以拥有只有 getter 的属性,这不会使其在 readonly 方面完全只读,但它将具有私有 setter。如果您想保留界面,则可以最接近它。所有属性的使用都会最大限度地在编译时给你警告。
  • 不要在问题中添加答案。只需发布您自己的答案或接受另一个答案。
  • 你能不能至少让我自己删除答案,现在我需要再写一遍.. :)
  • 您只需点击上面的“已编辑 # 分钟前”链接即可查看您发布的帖子,然后将其复制/粘贴到答案中。

标签: c# interface protected datamember


【解决方案1】:

C#接口没有状态,你不能在接口中声明字段不可写非只读。事实证明,要保持状态,您需要类,因此在您的情况下,它应该是基类或具体类...

一种方法是在接口中声明一个get属性,这将强制实现该接口的所有类提供get

public interface IHasApiKey
{
    string ApiKey {get;}
}

类应该是这样的

public class SomeFoo : IHasApiKey
{
    private readonly string _apiKey;
    public SomeFoo(string apiKey)
    {
        _apiKey = apiKey;
    }

    public string ApiKey => _apiKey;
}

【讨论】:

  • 这并不能确保实现不会包含setter。
  • 这在语法上无效。使用public string ApiKey =&gt; _apiKey 或仅使用public string ApiKey { get; } 并在构造函数中设置属性(使用自动生成的支持字段)。
  • @lior:它确实确保派生类不会,这已经很好了。接口本身不能强制实现有一个setter,不——接口只强制实现有什么。
  • @JeroenMostert:我知道,但答案没有回答问题
  • @lior,将私有字段设为只读,现在它不能有 setter ...
【解决方案2】:

更新[关闭]

看来我受到语言选择的限制。 C# 没有办法完成我想要的。最好的办法是允许装饰界面的设置器:

public interface IHasApiKey
{
    protected string _apiKey { get; readonly set; }
}

但这可能需要对编译器进行不可能的更改,并且可能会破坏语言设计。我发现它不太可能被添加。

感谢所有花时间思考这个问题的人!

【讨论】:

    猜你喜欢
    • 2011-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-26
    相关资源
    最近更新 更多