Skip to main content

C# Interface Properties

In C# interfaces, properties provide a powerful way to define getters and setters that implementing classes must provide. They are an essential part of interface design, allowing you to specify read-only, write-only, or read-write behaviors without dictating the implementation details.

Introduction to Interface Properties​

Interface properties define a contract that implementing classes must fulfill. They specify what properties a class must expose, but not how those properties should work internally. Properties in interfaces act similarly to methods but use property syntax for more intuitive data access.

Since C# 8.0, interfaces can also include default implementations for properties, making them even more versatile.

Basic Syntax for Interface Properties​

Here's the basic syntax for defining properties in an interface:

csharp
interface IMyInterface
{
// Property declaration
Type PropertyName { get; set; }
}

The implementing class must provide the property with the same name, type, and accessibility modifiers.

Types of Interface Properties​

Read-only Properties​

Read-only properties only have a getter:

csharp
interface IReadOnlyExample
{
string Name { get; }
}

class Employee : IReadOnlyExample
{
public string Name { get; private set; }

public Employee(string name)
{
Name = name;
}
}

In this example, the implementing class must provide a way to read the Name property but can control how it's set internally.

Write-only Properties​

Write-only properties only have a setter:

csharp
interface IWriteOnlyExample
{
string Password { set; }
}

class User : IWriteOnlyExample
{
private string _password;

public string Password
{
set { _password = value; }
}

public bool ValidatePassword(string attempt)
{
return _password == attempt;
}
}

Write-only properties are less common but useful for cases like passwords or other sensitive information.

Read-write Properties​

Read-write properties have both getters and setters:

csharp
interface IPerson
{
string FirstName { get; set; }
string LastName { get; set; }
int Age { get; set; }
}

class Student : IPerson
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}

Auto-implemented Properties in Implementing Classes​

When implementing interface properties, you can use auto-implemented properties for clean, concise code:

csharp
interface IProduct
{
int ProductId { get; set; }
string Name { get; set; }
decimal Price { get; set; }
}

class Book : IProduct
{
// Auto-implemented properties
public int ProductId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }

// Additional properties specific to Book
public string Author { get; set; }
public int Pages { get; set; }
}

Custom Implementation of Interface Properties​

You can also provide custom implementations with backing fields:

csharp
interface ITemperature
{
double Celsius { get; set; }
double Fahrenheit { get; set; }
}

class TemperatureConverter : ITemperature
{
private double _celsius;

public double Celsius
{
get { return _celsius; }
set { _celsius = value; }
}

public double Fahrenheit
{
get { return (_celsius * 9 / 5) + 32; }
set { _celsius = (value - 32) * 5 / 9; }
}
}

// Usage example:
static void Main()
{
TemperatureConverter temp = new TemperatureConverter();

// Set Celsius, read both
temp.Celsius = 25;
Console.WriteLine($"Celsius: {temp.Celsius}°C, Fahrenheit: {temp.Fahrenheit}°F");
// Output: Celsius: 25°C, Fahrenheit: 77°F

// Set Fahrenheit, read both
temp.Fahrenheit = 50;
Console.WriteLine($"Celsius: {temp.Celsius}°C, Fahrenheit: {temp.Fahrenheit}°F");
// Output: Celsius: 10°C, Fahrenheit: 50°F
}

This example shows a custom implementation where changing one property automatically updates the related property through conversion.

Default Interface Properties (C# 8.0+)​

Starting with C# 8.0, interfaces can include default implementations for properties:

csharp
interface IShape
{
double Area { get; }
double Perimeter { get; }

// Default property with implementation
string Description => $"Shape with area {Area} and perimeter {Perimeter}";
}

class Circle : IShape
{
public double Radius { get; }

public Circle(double radius)
{
Radius = radius;
}

public double Area => Math.PI * Radius * Radius;
public double Perimeter => 2 * Math.PI * Radius;

// No need to implement Description as it uses the default implementation
}

// Usage:
static void Main()
{
Circle circle = new Circle(5);
Console.WriteLine(circle.Description);
// Output: "Shape with area 78.54... and perimeter 31.42..."
}

Interface Properties vs. Abstract Properties​

It's important to understand the difference between interface properties and abstract properties:

csharp
// Interface property
interface IVehicle
{
int Speed { get; set; }
}

// Abstract property
abstract class Vehicle
{
public abstract int Speed { get; set; }
}

Key differences:

  • Interfaces can be implemented by any class, but a class can only inherit from one abstract class
  • Abstract classes can include implementation details, while traditional interfaces only define the contract
  • Abstract classes can have constructors and fields, which interfaces cannot

Explicit Interface Implementation​

Sometimes you might want to implement an interface property explicitly:

csharp
interface ILoggable
{
string LogData { get; }
}

interface IPrintable
{
string LogData { get; } // Same property name as in ILoggable
}

class Report : ILoggable, IPrintable
{
// Explicit interface implementation
string ILoggable.LogData => "Detailed log data for logging purposes";
string IPrintable.LogData => "Formatted data for printing";

// Regular properties
public string Title { get; set; }
}

// Usage example:
static void Main()
{
Report report = new Report();
report.Title = "Annual Report";

// Need to cast to access explicit implementation
Console.WriteLine(((ILoggable)report).LogData);
// Output: "Detailed log data for logging purposes"

Console.WriteLine(((IPrintable)report).LogData);
// Output: "Formatted data for printing"
}

Explicit implementation helps resolve naming conflicts and keeps the class's public API clean.

Real-world Example: Building a Content Management System​

Let's see how interface properties can be used in a simple content management system:

csharp
// Base interfaces
interface IContent
{
string Title { get; set; }
string Author { get; set; }
DateTime PublishDate { get; set; }
bool IsPublished { get; set; }
}

interface ICommentable
{
List<string> Comments { get; }
void AddComment(string comment);
}

interface IVersionable
{
int Version { get; }
void UpdateVersion();
}

// Implementation
class Article : IContent, ICommentable, IVersionable
{
private int _version = 1;

// IContent implementation
public string Title { get; set; }
public string Author { get; set; }
public DateTime PublishDate { get; set; }
public bool IsPublished { get; set; }

// ICommentable implementation
public List<string> Comments { get; } = new List<string>();

public void AddComment(string comment)
{
Comments.Add(comment);
}

// IVersionable implementation
public int Version => _version;

public void UpdateVersion()
{
_version++;
}

// Additional functionality
public string Content { get; set; }
}

// Usage example:
static void Main()
{
Article article = new Article
{
Title = "Understanding C# Interfaces",
Author = "Jane Developer",
PublishDate = DateTime.Now,
IsPublished = true,
Content = "Interfaces in C# provide a powerful way to..."
};

// Using the commentable feature
article.AddComment("Great article!");
article.AddComment("Very helpful explanation.");

// Using the versionable feature
article.UpdateVersion();

Console.WriteLine($"Article: {article.Title} by {article.Author}");
Console.WriteLine($"Published: {article.PublishDate}, Version: {article.Version}");
Console.WriteLine($"Comments ({article.Comments.Count}):");
foreach (var comment in article.Comments)
{
Console.WriteLine($"- {comment}");
}

// Output:
// Article: Understanding C# Interfaces by Jane Developer
// Published: [current date/time], Version: 2
// Comments (2):
// - Great article!
// - Very helpful explanation.
}

This example shows how interfaces with properties help model a system with clear separation of concerns and flexible implementation.

Best Practices for Interface Properties​

  1. Keep interfaces focused: Each interface should represent a single responsibility or capability.

  2. Use read-only properties where applicable to enforce immutability:

    csharp
    interface IReadOnlyCustomer
    {
    int Id { get; }
    string Name { get; }
    }
  3. Consider default implementations (C# 8.0+) for calculated properties:

    csharp
    interface IPerson
    {
    string FirstName { get; set; }
    string LastName { get; set; }
    string FullName => $"{FirstName} {LastName}";
    }
  4. Document expected behavior, especially for properties with complex semantics:

    csharp
    interface ICacheable
    {
    /// <summary>
    /// Time in seconds that the item should remain in cache.
    /// Value of 0 means no caching, -1 means cache indefinitely.
    /// </summary>
    int CacheDuration { get; set; }
    }

Common Pitfalls and Solutions​

Pitfall 1: Conflicting Property Signatures​

When multiple interfaces define the same property but with different types or accessibility:

csharp
interface IA
{
string Value { get; set; }
}

interface IB
{
int Value { get; set; } // Same name but different type
}

// This won't compile!
class MyClass : IA, IB
{
public string Value { get; set; } // Can't satisfy both interfaces
}

Solution: Use explicit interface implementation:

csharp
class MyClass : IA, IB
{
// Explicitly implement both interfaces
string IA.Value { get; set; }
int IB.Value { get; set; }
}

Pitfall 2: Interface Properties vs. Class Access Modifiers​

An interface property might need different access levels in the implementing class:

csharp
interface IConfigurable
{
string ConnectionString { get; set; }
}

class DatabaseService : IConfigurable
{
// Need to make this publicly accessible to satisfy the interface
// but want to restrict who can set it
public string ConnectionString { get; private set; }

public DatabaseService(string connectionString)
{
ConnectionString = connectionString;
}
}

Summary​

Interface properties are a powerful feature in C# that allow you to:

  • Define contracts for data access that implementing classes must follow
  • Specify read-only, write-only, or read-write behaviors
  • Create flexible designs with clear separation of concerns
  • Enable polymorphism through interface types

The ability to define properties in interfaces makes C# interfaces particularly expressive and useful for designing clean, maintainable systems. With C# 8.0's default interface members, interfaces become even more powerful by allowing you to provide default implementations for properties.

Exercises​

  1. Create an interface called IEmployee with properties for Name, Id, and Salary. Implement this interface in a class called FullTimeEmployee.

  2. Design an interface hierarchy for a file system, with interfaces like IFile (with properties like Name, Size, CreationDate), IDirectory, etc. Implement these interfaces for a simple in-memory file system.

  3. Create an interface with a property that has a default implementation (C# 8.0+). Override this default in an implementing class.

  4. Design an interface with both read-only and read-write properties, then implement it in a class that adds appropriate validation logic.

Additional Resources​



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)