DEV Community

Sam Ferree
Sam Ferree

Posted on

No Sacred Cows: I, Interface

The Argument

Why should we prefix the names of interfaces with I in C#? Here is the full answer given from Microsoft's documentation on C# coding standards. To save you the click, I'll post it here.

When naming an interface, use pascal casing in addition to prefixing the name with an I. This clearly indicates to consumers that it's an interface.

So we need to prefix the name of the type with I so that consumers of the type know that it's an interface. Seems simple and straightforward, but already this rule fails the consistency test. I could not find any suggestions that we preface class names with C and structs with S and records with R in order to indicate these things to consumers.

So the question then is, Why do consumers need to know they are using an interface, and why do consumers not need to know they are using a struct, class, or record?

Do Consumers need to know they are using an interface?

As far as I can tell you will encounter an interface in one of exactly three places:

  1. You are defining the interface
  2. You are implementing the interface
  3. You are working with a variable that is of an interface type

Let's examine, in each of these three scenarios, how helpful it is to know that you are working with an interface, and how valuable that information is.

Defining an interface

public interface Entity
{
  string Id { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Is it clear when defining this type, that it's an interface? Hopefully the word interface preceding the type name is sufficient to communicate that information to us.

Implementing an interface

public class Book : Entity
{
}
Enter fullscreen mode Exit fullscreen mode

Is it clear when implementing the Entity type that it is an interface? Not Just by looking at it. In fact, we won't notice anything at all until we've run the compiler. Once we've run the compiler we will be informed that we've failed to correctly implement the interface.

But how soon after we type this code is a compilation or language service run? In many IDEs very shortly. The editors that I'm familiar with will not only alert me that I need to do something, they will provide a macro or tool tip to do it quickly (ctrl+. in visual studio).

In fact, whether or not Entity is an interface, or an abstract base class my process is the same. Wait for the red squiggles to appear and then using the auto-complete features to stub out the abstract members I need to implement.

So in this way, In the case that your editor settings don't provide color indicators for the information, You don't actually need to know whether or not you've implemented an interface or extended an abstract base class. The compiler knows there are things you have to add to your type definition.

"But Wait!" you say, "What about multiple inheritance?"

Ah yes, well any given type in C# can have any number of interfaces, but only one base type. Without the I how will we know which one is the base type and which one is the interface?

The answer is again, the compiler. If you're using a modern editor you will likely know very quickly if you've extended multiple base classes, whether by color coding, or language features running error checking as you type. In this case, I don't need an I in front of my interface name any more than I need an A in front of abstract class names.

Working with a variable of an interface type

What if you get a variable back from a function, and the type is Entity and you aren't sure whether that type is an interface or a concrete type?

Even if you are not using an editor that can quickly communicate that information, I would argue that it makes no difference. You don't care if that type is an interface. You care what that type tells you about what methods are on it, and what methods it can be passed into as a parameter. That information is not only quickly available to you in most modern editors, but it's also impossible to determine any of it by whether or not there is an I at the beginning of it's name.

Following the convention

Okay, so maybe it's actually not that helpful, but is there any value in following the convention? There very well could be, but that's not the whole question. The question is: "at what cost?"

If Naming things is hard, Why make it harder? We either end up adding I's to everything or you have long descriptive names using I to describe what an object can do, i.e. ISaveBooks

Sometimes it's common to use the I to distinguish The interface from The implementation. These types often come in pairs: IBookRepository, BookRepository. The idea here being that we can code against the interface, not the concrete type. There are many good reasons to do this, and nearly all of them go away if our interface has only one implementation. In such a case, the ceremony is all noise and no signal. The interface defines what the type can do, but the name of the implementation doesn't communicate anything to us. What would be useful to see is a descriptive name of how, such as SqlBookRepository, or InMemoryBookRepository. In this, far more clear, example we no longer need to preface our interface with I. The interface is BookRepository and we have two implementations, one that reads and writes books from a SQL database, and one that keeps them in memory. For this problem, I much more likely to do something like this:

public interface BookWriter
{
  Task Save(Book book)
}

public interface BookReader
{
  Task<Book> Load(string bookId)
}

public class SqlBookRepository : BookWriter, BookReader { ... }
public class InMemoryBookRepository : BookWriter, BookReader { ... }
Enter fullscreen mode Exit fullscreen mode

Aside from the clarity, I no longer need to force a service which only reads books to be bound to the functionality around saving them. If I have any other functionality I might want to insert using decorators, like publishing an event every time a book is created or updated, I can do so without needing to write an empty pass through on the read part of the repository.

For me, I've not found any argument to prefix my interface names with I any more compelling than the idea that I should prefix my class names with C. It's an old convention, a relic of Hungarian notation, something which even other parts of the recommended C# coding conventions have gone away from.

Find this article and more on my substack

Discussion (10)

Collapse
jayjeckel profile image
Jay Jeckel

I think it makes more sense when combined with the convention of prefixing abstract classes with A. Then the differences of IThing, AThing, and Thing can be made apparent with a single letter. Incorporate an E prefix for enums and it becomes a bit more consistent than the lone cargo cult ritual of interfaces have to start with I. Sure, the convention does have a touch of Systems Hungarian to it, but in my experience it can be a useful way to name things.

As for why classes and structs don't get prefixes, as I was taught back in the days of rampant Hungarian notation, it's because they can be instanced and the prefixes are to differentiate the instantiable types from the non-instantiable types

Collapse
sam_ferree profile image
Sam Ferree Author

I'm curious as to the use case for needing to know whether a type is concrete or abstract just by looking at it?

Even if most modern editors couldn't give you this information right away, when would you need to create an instance of a type you know nothing about? when could you hope to do so correctly?

Collapse
jayjeckel profile image
Jay Jeckel

It isn't about readability at write-time, it's about readability at read-time. Anything that helps me understand the design of the system when I'm reading through the code is useful.

Take your example:

public class SqlBookRepository : BookWriter, BookReader { ... }
Enter fullscreen mode Exit fullscreen mode

That line of code tells me there are two types being used to derive a third type. Is the derived type based on a partial implementation (ie abstract class) and a contract (ie an interface) or is it based on two contracts? Can't tell here, so I'm going to have to dig further into the code to find out. However, a couple additional characters would answer the question without further digging. Is such information useful in toy examples like those in your article, arguably not really. Is it useful when digging through a project with hundreds of files and tens of thousands of lines of code? Yes, yes it is.

If all your doing is writing code, then I can understand finding such prefixes less than useful, but if you spend a major portion of life digging through other's code, then these little bits of context help.

And ultimately, that's the point, providing context about the purpose of a type. The I isn't a Systems Hungarian notation saying the type is implemented as an interface, it's an Apps Hungarian notation saying the type's purpose is as a contract for the api of the derived type.

Thread Thread
sam_ferree profile image
Sam Ferree Author • Edited on

Note that your original point was about knowing whether or not you could instantiate something, which is done while writing code, and now you've seemed to switch gears saying it's about reading code.

I think IFF you're working in an editor where the color of abstract classes and interfaces are exactly the same, and where some information about the type doesn't come up when you hover over it and if you can't jump to the relevant file quickly with F12 or some similar keystrokes, THEN it could be useful to have that information.

But if you're working in an environment that limited, all of hungarian notation comes back on the table. What type is this variable? You can't know because you've told me your editor doesn't display that information and you can't jump to it quickly, so by your own argument it makes sense to preface strings with 'sz'

Is this a member variable? You can't know because you've told me your editor doesn't provide that information or a quick way to find it out. So we should provide that information be prefixing member variables with an '_'

So IFF that is your situation and you follow all of Hungarian notation rules, than I would say you're being consistent and writing code for good readability, but if not, then it sounds like cargo cult programming to me.

Thread Thread
jayjeckel profile image
Jay Jeckel

Note that your original point was about knowing whether or not you could instantiate something, which is done while writing code, and now you've seemed to switch gears saying it's about reading code.

Ah, I see the confusion. I wasn't suggesting that knowing what is concrete is the purpose of the convention, merely relating that, as I heard it back in the day, instantiable classes were considered the default and that is why non-concrete things get prefixes and instantiable stuff doesn't.

If there is a purpose to it, then that is to tell the coder what the job of the types is. No prefix, the types job is to be instantiated; I prefix, the types job is as a contract; A prefix, the job is as a base partial implementation; etc.

I think IFF you're working in an editor where the color of abstract classes and interfaces are exactly the same, and where some information about the type doesn't come up when you hover over it and if you can't jump to the relevant file quickly with F12 or some similar keystrokes, THEN it could be useful to have that information.

Maybe I'm getting old, but I don't find editor features to be a compelling argument for or against naming conventions. Others' experiene may be different, but I still read a good amount of code daily outside IDEs and the readablity of the code shouldn't be downgrade because it isn't be read in an editor.

But if you're working in an environment that limited, all of hungarian notation comes back on the table. What type is this variable? You can't know because you've told me your editor doesn't display that information and you can't jump to it quickly, so by your own argument it makes sense to preface strings with 'sz'

That's what I was getting at in the last post. The I prefix isn't Systems Hungarian telling you the type is implemented as a interface construct, it is Application Hungarian telling you the type is an API contract. For example, IEntity would be equivalent to EntityContract.

As another example, I don't need to prefix strings with s or other such, because their underlying type isn't what is important, their purpose is important, so their name would have DisplayText, FirstName, or some other descriptive name to make that clear.

Is this a member variable? You can't know because you've told me your editor doesn't provide that information or a quick way to find it out. So we should provide that information be prefixing member variables with an '_'

Yep, in my set of naming conventions, all private member variables are prefixed with _. And at this point it probably won't surprise you to find out that I also enforce the use of this/self scoping when calling member fields, methods, and properties.

So IFF that is your situation and you follow all of Hungarian notation rules, than I would say you're being consistent and writing code for good readability, but if not, then it sounds like cargo cult programming to me.

Yes, I religiously follow very consistent naming conventions and often reevaluate them for the dangers of cargo coding, but these have proved useful to me time and again.

Collapse
miketalbot profile image
Mike Talbot

Not that I totally disagree with the point you make about clarity, but I've always considered specially naming interfaces and abstract classes like this because you cannot create them. I can do new Dictionary but I immediately know I have to look for something to create or be an implementation if it starts with an "I" or an "A"

Thread Thread
sam_ferree profile image
Sam Ferree Author

I think that information is present, but insufficient to determine how you can obtain an instance of a given type.

A class might have a private constructor. In this case an S prefix to signal a struct actually would seem to be useful because it would tell you there has to be a public parameter-less constructor.

But that's not always true either. Consider the Guid type. new Guid() is almost never correct, and what you actually want is Guid.NewGuid().

If IEnumerable were just named Enumerable, would you just new them up? The kind of enumerable you need often depends on the situation. Creating a range happens under different circumstances than creating a List. Knowing that you can't call new IEnumerable is insufficient to tell you how to actually get one.

My point is that there's many things you need to know when trying to instantiate a given type. If you know so little about a type that you aren't sure if it's an interface or a class, then I wonder how you could know how to correctly instantiate it, or even why you need to. And all of this Even If this information wasn't quickly available to us in most modern editors.

Collapse
stphnwlsh profile image
Stephen Walsh

Love this! Have moved away from I interfaces in a couple of projects I've worked on. It forces you to be mindful of naming.

Had a vehicle and vessel concrete types and the team settled on carrier as the interface. It was at the very least a fun discussion

Collapse
larsonnn profile image
Lars Feldeisen
  • They create a consistent look to the code, so that readers can focus on content, not layout.

Look at my name and tell me what I'm. That is also how you can structure projects. When looking at the filename and folder in context you should already know what this is.

You see IBookRepository and knows what that is. Thats the whole reason. Analyzing the layout distracts from programming. When working in multiple projects it's really helpful not to think a second about what something is.

Collapse
sam_ferree profile image
Sam Ferree Author

When you say "what this is" do you mean you should be able determine domain specific information about what's in the file? If so I agree.

If you're saying I need to know whether the file contains an interfaces or some other type, then I'm not really sure you've provided anything sufficient to convince me why. If I see ScheduleWriter and ScheduleReader I don't need to spend any more time than I do when I see IScheduleRepository. I will either need to examine those files or not based on whether or not I need to persist Schedules.

If I don't need to persist Schedules, I can ignore ScheduleWriter and ScheduleReader as easily as I can ignore IScheduleRepository.

If I do need to persist Schedules, then I need to look at those files to see what the API is.