Как иметь C# интерфейс с участником только для чтения - PullRequest
2 голосов
/ 12 февраля 2020

Неудобство интерфейса

Недавно я обнаружил, что нуждается в чем-то, что должно быть очень возможно в C# (я знаю, что это в C ++): нескольким классам нужен ключ API, который абсолютно должен быть частным, неизменным полем (за исключением того, что оно объявлено в конструкторе). Чтобы избежать дублирования кода, я хотел создать интерфейс для классов, которым требуется ключ API.

Я позволю коду говорить за себя:

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

Проблемы:

  • Вместо этого я мог бы позволить ему быть классом, поскольку интерфейсы не могут создавать экземпляры атрибутов-членов. Но поскольку C# не допускает множественное наследование, и, поскольку я считаю эту функцию поведением, а не реализацией, я не понимаю, почему это не должен быть интерфейс. И это может быть sh с классами, которые уже имеют базовый класс.
  • Я мог бы преобразовать его в свойство, но независимо от уровня доступности, если он унаследован, он все еще может быть изменен в методах производные классы, тогда как я действительно хочу поведение readonly. (const, но может быть установлен в конструкторе)
  • Я обнаружил атрибут 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); }
};

Объясню должным образом? И у кого-нибудь есть идеи, как решить это элегантно? Заранее большое спасибо.

Ответы [ 2 ]

3 голосов
/ 12 февраля 2020

C# интерфейсы не имеют состояния, вы не можете объявить поле в интерфейсе как недоступное для записи и не только для чтения. И получается, что для сохранения состояния вам нужен класс, поэтому в вашем случае это должен быть базовый класс или конкретный ...

Один из способов go - объявить свойство 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;
}
0 голосов
/ 12 февраля 2020

Обновление [Закрыто]

Кажется, я ограничен в выборе языка. C# не может выполнить то, что я хочу. Лучше всего было бы разрешить украшать установщик интерфейса:

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

Но, вероятно, это потребовало бы невозможных изменений в компиляторе и, вероятно, порвало бы с языковым дизайном. Я считаю маловероятным, что он когда-либо будет добавлен.

Спасибо всем, кто нашел время подумать об этом!

...