When to Use Interface vs Abstract Class in C#
Interfaces define a contract of what a class can do (pure abstraction with no implementation). Abstract classes provide a common base with shared implementation that derived classes can extend.
Use interfaces when you need multiple inheritance, want to define capabilities across unrelated classes, or need maximum flexibility. Use abstract classes when you have common implementation to share among related classes.
C# supports multiple interface implementation but only single inheritance from a class. Abstract classes can have constructors, fields, and both abstract and concrete members.
Defining Interfaces and Abstract Classes
using System;
using System.Collections.Generic;
// Interface - defines a contract (what something can do)
public interface IPaymentProcessor
{
void ProcessPayment(decimal amount);
bool ValidatePayment(decimal amount);
}
// Another interface - supports multiple implementation
public interface IRefundable
{
void ProcessRefund(decimal amount);
}
// Abstract class - provides common implementation
public abstract class PaymentBase
{
// Shared fields
protected string TransactionId { get; set; }
protected DateTime TransactionDate { get; set; }
// Constructor
protected PaymentBase()
{
TransactionId = Guid.NewGuid().ToString();
TransactionDate = DateTime.Now;
}
// Concrete method - shared implementation
public void LogTransaction(string message)
{
Console.WriteLine($"[{TransactionDate:HH:mm:ss}] {TransactionId}: {message}");
}
// Abstract method - must be implemented by derived classes
public abstract string GetPaymentMethod();
}Implementing Interface and Inheriting from Abstract Class
A class can inherit from one abstract class and implement multiple interfaces, combining shared implementation with flexible contracts.
// Concrete class implementing interface
public class CreditCardProcessor : PaymentBase, IPaymentProcessor, IRefundable
{
private string cardNumber;
public CreditCardProcessor(string cardNumber)
{
this.cardNumber = cardNumber;
}
public override string GetPaymentMethod()
{
return "Credit Card";
}
public void ProcessPayment(decimal amount)
{
LogTransaction($"Processing ${amount} via {GetPaymentMethod()}");
Console.WriteLine($"Charging card ending in {cardNumber.Substring(cardNumber.Length - 4)}");
}
public bool ValidatePayment(decimal amount)
{
// Validation logic
bool isValid = amount > 0 && amount < 10000;
LogTransaction($"Validation: {(isValid ? "Passed" : "Failed")}");
return isValid;
}
public void ProcessRefund(decimal amount)
{
LogTransaction($"Refunding ${amount}");
}
}
// Another concrete class
public class PayPalProcessor : PaymentBase, IPaymentProcessor
{
private string email;
public PayPalProcessor(string email)
{
this.email = email;
}
public override string GetPaymentMethod()
{
return "PayPal";
}
public void ProcessPayment(decimal amount)
{
LogTransaction($"Processing ${amount} via {GetPaymentMethod()}");
Console.WriteLine($"Charging PayPal account: {email}");
}
public bool ValidatePayment(decimal amount)
{
bool isValid = amount > 0 && !string.IsNullOrEmpty(email);
LogTransaction($"Validation: {(isValid ? "Passed" : "Failed")}");
return isValid;
}
}Interface-Only Implementation
Unrelated classes can implement the same interface without inheriting from a common base class, providing flexibility.
// Unrelated class can also implement the interface
public class BitcoinProcessor : IPaymentProcessor
{
private string walletAddress;
public BitcoinProcessor(string walletAddress)
{
this.walletAddress = walletAddress;
}
public void ProcessPayment(decimal amount)
{
Console.WriteLine($"Sending ${amount} worth of Bitcoin to {walletAddress}");
}
public bool ValidatePayment(decimal amount)
{
return amount > 0 && walletAddress.Length == 34;
}
}Using Polymorphism with Interfaces and Abstract Classes
// Polymorphism via interface
List<IPaymentProcessor> processors = new List<IPaymentProcessor>
{
new CreditCardProcessor("1234-5678-9012-3456"),
new PayPalProcessor("user@example.com"),
new BitcoinProcessor("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa")
};
Console.WriteLine("=== Processing payments via interface ===");
foreach (var processor in processors)
{
if (processor.ValidatePayment(150))
{
processor.ProcessPayment(150);
}
Console.WriteLine();
}
// Using abstract class features
Console.WriteLine("=== Using abstract class features ===");
var creditCard = new CreditCardProcessor("9876-5432-1098-7654");
Console.WriteLine($"Payment method: {creditCard.GetPaymentMethod()}");
creditCard.ProcessPayment(250);
// Multiple interfaces
Console.WriteLine("\n=== Multiple interface implementation ===");
IRefundable refundable = creditCard;
refundable.ProcessRefund(50);