Interface
Interface represents a contract. A set of public methods any implementing class has to have.
- Provides specification rather than an implementation for its members.
- Interface members are all implicitly abstract
- A class or struct can implement multiple interfaces
- Interface can contain methods, properties, events and indexers
- Interface members are always implicitly public and cannot declare an access modifier.
- Implementing interface means providing a public implementation for all its members.
public interface IEnumerator { bool MoveNext(); object Current { get; } void Reset(); } // Here's a class that implements this interface: class Countdown : IEnumerator { int count = 11; public bool MoveNext () => count-- > 0; public object Current => count; public void Reset() { throw new NotSupportedException(); } } static void Main() { IEnumerator e = new Countdown(); while (e.MoveNext()) Console.Write (e.Current); // 109876543210 }
Extending an Interface
Interface can be extended just like class, it can derive from other interfaces, one or many interfaces.
public interface IUndoable { void Undo(); } public interface IRedoable : IUndoable { void Redo(); } static void Main() { IRedoable r = null; IUndoable u = r; }
Types that implement IRedoable must also implement the memebers of IUndoable.
Explicit Interface Implementation
When implementing multiple interfaces can result in member signature collision. It can be resolved by explicitly implementing an interface member. The only way to call the explicitly implemented member will be to cast to the interface. It be best used to hide members that are highly specialized and distracting to a type’s normal use case.
interface I1 { void Foo(); } interface I2 { int Foo(); } public class Widget : I1, I2 { public void Foo () { Console.WriteLine ("Widget's implementation of I1.Foo"); } int I2.Foo() { Console.WriteLine ("Widget's implementation of I2.Foo"); return 42; } } static void Main() { Widget w = new Widget(); w.Foo(); // Widget's implementation of I1.Foo ((I1)w).Foo(); // Widget's implementation of I1.Foo ((I2)w).Foo(); // Widget's implementation of I2.Foo }
Implementing Interface Members Virtually
- All implicit interface member implemented by default are sealed.
- It must be marked as virtual or abstract in base class to be overridden.
- Calling the interface member through base class or the interface calls the subclass implementation.
- Explicitly implemented interface member cannot be marked as virtual or overridden but it can be reimplemented.
public interface IUndoable { void Undo(); } public class TextBox : IUndoable { public virtual void Undo() => Console.WriteLine ("TextBox.Undo"); } public class RichTextBox : TextBox { public override void Undo() => Console.WriteLine ("RichTextBox.Undo"); } static void Main() { // Calling the interface member through either the base class or the interface // calls the subclass’s implementation: RichTextBox r = new RichTextBox(); r.Undo(); // RichTextBox.Undo ((IUndoable)r).Undo(); // RichTextBox.Undo ((TextBox)r).Undo(); // RichTextBox.Undo }
Reimplementing an Interface in a Subclass
- A subclass can reimplement any interface member already implemented by a base class.
- Reimplmentation works wheather member is virtual or not in the base class.
- Reimplementation also works for member implemented implicitly or explicitly.
The example below shows that TextBox class implements IUndoable interface but implements Undo method explicitly since the method in IUndoable interface is not marked as virtual therefore it needs to be implemented explicitly.
The RichTextBox inherits from TextBox and IUndoable but since the method is not declared virtual in TextBox class therefore it needs to reimplement interface IUndoable Undo() method.
public interface IUndoable { void Undo(); } public class TextBox : IUndoable { void IUndoable.Undo() => Console.WriteLine ("TextBox.Undo"); } public class RichTextBox : TextBox, IUndoable { public new void Undo() => Console.WriteLine ("RichTextBox.Undo"); } static void Main() { // Calling the reimplemented member through the interface calls the subclass’s implementation: RichTextBox r = new RichTextBox(); r.Undo(); // RichTextBox.Undo Case 1 ((IUndoable)r).Undo(); // RichTextBox.Undo Case 2 }
Drawbacks of reimplementation
- The subclass has no way to call the base class method.
- The base class author may not anticipate that a method be reimplemented and
may not allow for the potential consequences.
Better Approach
- Better design the base class so that reimplementation is not required.
- Mark virtual when implicitly implementing a member.
- If you don’t want any further reimplementation, mark it as sealed.
- Use the following pattern when implementing member explicitly.
public interface IUndoable { void Undo(); } public class TextBox : IUndoable { //Calls method below void IUndoable.Undo() => Undo(); protected virtual void Undo() => Console.WriteLine( "TextBox.Undo" ); } public class RichTextBox : TextBox { protercted override void Undo() => Console.WriteLine( "RichTextBox.Undo" ); } static void Main() { IUndoable r = new RichTextBox(); r.Undo(); // RichTextBox.Undo }
Interfaces and Boxing
Casting a struct to an interface causes boxing and calling an implicitly implemented member on a struct does not cause boxing.
interface I { void Foo(); } struct S : I { public void Foo() {} } static void Main() { S s = new S(); s.Foo(); // No boxing. I i = s; // Box occurs when casting to interface. i.Foo(); }
Class vs Interface
- Use classes and subclasses for types that naturally share an implementation.
- Use interfaces for types that have independent implementations.
Abstract class vs Interface
Abstract
- Abstract class can have abstract members and also non abstract members.
- Members of abstract can have protected parts, static, etc.
- A class can inherit only one abstract class.
- Abstract classes can add more functionality without destroying the child classes.
Interface
- All the members are implicitly abstract and all the members must override to its derived class.
- Members of the interface are public with no implementation .
- A class can inherit one or more interfaces.
- Modifying interface can have problems with derived classes.
Abstract Class
public abstract class CountryTaxCalculator { public abstract decimal CalculateTaxAmount(); } public class TaxCalculatorForUS : CountryTaxCalculator { public override deciaml CalculateTaxAmount() { .... } } public class TaxCalculatorForUK : CountryTaxCalculator { public override deciaml CalculateTaxAmount() { .... } } public class TaxCalculatorForIN : CountryTaxCalculator { public override deciaml CalculateTaxAmount() { .... } }
Interface Example
public interface ICountryTaxCalculator { decimal CalculateTaxAmount(); } public class TaxCalculatorForUS : ICountryTaxCalculator { public deciaml CalculateTaxAmount() { .... } } public class TaxCalculatorForUK : ICountryTaxCalculator { public deciaml CalculateTaxAmount() { .... } } public class TaxCalculatorForIN : ICountryTaxCalculator { public deciaml CalculateTaxAmount() { .... } }
Abstract classes contains code of their own and interfaces provide a set of property and method signatures with no implementation code at all.