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 [email protected]. I’d greatly appreciate your feedback! :)