3

Interface inconvenience

I recently found myself in need of something, which should very much be possible in C# (I know it is in C++): Several classes need an api key, which absolutely has to be a private, immutable field (except for being declared in the constructor). To avoid code duplication, I wanted to create an interface for classes that need an api key.

I will let the code speak for itself:

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

Problems:

  • I could let it be a class instead, since interfaces cannot instantiate member attributes. But since C# does not allow multiple inheritance, and since I consider this feature a behaviour rather than an implementation, I can't see why it shouldn't be an interface. And it might clash with classes which already have a base class.
  • I could convert it into a property, but no matter the accessibility level, if it is inherited, it can still be modified in methods of derived classes, whereas I really want the behaviour of readonly. (const, but can be set in the constructor)
  • I discovered the attribute System.ComponentModel.ReadOnlyAttribute, but documentation is very limited, and it doesn't look like it performs like readonly, but more like an attribute which can be queried for in user code.
  • If I convert it to an auto-property, then all derived classes need to specify a private data member to point to, which again means duplicate code (which I try to avoid by having this interface in the first place)

For completeness' sake, here is what I imagine the correct code would look like in 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); }
};

Do I explain properly? And does anyone have an idea of how to solve this elegantly? Thanks a lot in advance.

LarsTech
  • 80,625
  • 14
  • 153
  • 225
alexpanter
  • 1,222
  • 10
  • 25
  • 1
    "And since I consider this feature a behaviour rather than an implementation, I can't see why it shouldn't be an interface." In C# an interface is a specific thing that only dictates what *clients* can do with an instance. It can never dictate how classes that expose the interface should implement it. In that sense, it's not up to you what to "consider" behavior. This does mean you need a base class to enforce this, and if a class doesn't like inheriting from that it'll have to re-do the implementation. You can use encapsulation and `sealed` instead if this is undesirable. – Jeroen Mostert Feb 12 '20 at 12:55
  • 1
    "If I convert it to an auto-property, then all derived classes need to specify a private data member to point to, which again means duplicate code (which I try to avoid by having this interface in the first place)" - Why not? you could have property with only getter, which won't make it fully readonly in the terms of readonly, but it will have private setter. Like it closest you can get if you want to stay with interface. All the attribute usages will maximum give you warning on compile time. – Roma Borodov Feb 12 '20 at 13:58
  • 1
    Don't put answers in the question. Just post your own answer or accept another one. – LarsTech Feb 12 '20 at 15:29
  • Could you at least have let me delete the answer myself, now I need to write it again.. :) – alexpanter Feb 12 '20 at 15:49
  • You could just click on the "edited # mins ago" link above and see the post you made and just copy / paste it to an answer. – LarsTech Feb 12 '20 at 15:58

2 Answers2

3

C# interfaces don't have a state, you can't declare field in interface non writable non read only. And it turns out that for keeping a state you need class, so in your case it should be base class, or concrete one...

One way to go is to declare a get property in interface, which will force all classes that implement this interface to provide get

public interface IHasApiKey
{
    string ApiKey {get;}
}

and classes should look like this

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

    public string ApiKey => _apiKey;
}
Arsen Mkrtchyan
  • 49,896
  • 32
  • 148
  • 184
  • 1
    This is not ensure that the implementation will not include a setter. – Lior Feb 12 '20 at 13:07
  • This is syntactically invalid. Use either `public string ApiKey => _apiKey`, or just `public string ApiKey { get; }` and set the property in the constructor (using an automatically generated backing field). – Jeroen Mostert Feb 12 '20 at 13:14
  • @lior: it does ensure derived classes won't, which is as good as it gets. The interface itself cannot enforce that an implementation should *not* have a setter, no -- interfaces only enforce what implementations *do* have. – Jeroen Mostert Feb 12 '20 at 13:25
  • @JeroenMostert: i know that, but the answer did not answer the question – Lior Feb 12 '20 at 13:28
  • @lior, made a private field readonly, now it cant have setter ... – Arsen Mkrtchyan Feb 12 '20 at 14:10
  • @JeroenMostert agree, fixed – Arsen Mkrtchyan Feb 12 '20 at 14:12
  • even with a setter, it can still be an auto-property, adhering to the interface, and breaking the read-only mode. But I also deducted this is the best scenario, though still not at all what I desired.. – alexpanter Feb 12 '20 at 15:01
0

Update [Closed]

It seems I am limited by my choice of language. C# has no way of accomplishing what I want. The best thing would be allow decorating the interface's setter:

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

But likely that would require impossible changes to the compiler, and likely break with the language design. I find it unlikely that it will ever be added.

Thank you everyone who took their time to think about this!

Community
  • 1
  • 1
alexpanter
  • 1,222
  • 10
  • 25