Leveraging the interface, collection, and object

Not only can classes and types apply an extension method, but interfaces, collections, and any other objects can be functionally extended using an extension method as well. We are going to discuss this in the upcoming sections.

Extending the interface

We can extend the method in an interface in the same way we extend the method in a class or type. We still need the public static class and the public static method. By extending the interface abilities, we can use the extension method just after we create it without the need to create the implementation inside the class that we inherit from the interface, since the implementation is done when we declare the extension method. Let's take a look at the following DataItem class, which we can find in the ExtendingInterface.csproj project:

namespace ExtendingInterface 
{ 
  public class DataItem 
  { 
    public string Name { get; set; } 
    public string Gender { get; set; } 
  } 
} 

We also have the following IDataSource interface:

namespace ExtendingInterface 
{ 
  public interface IDataSource 
  { 
    IEnumerable<DataItem> GetItems(); 
  } 
} 

As we can see, the IDataSource interface has only one signature of the method, which is named GetItems(), returning IEnumerable<DataItem>. Now, we can create a class to inherit the IDataSource interface, which we give a name, ClubMember; it has the implementation of the GetItems() method, as follows:

public partial class ClubMember : IDataSource 
{ 
  public IEnumerable<DataItem> GetItems() 
  { 
    foreach (var item in DataItemList) 
    { 
      yield return item; 
    } 
  } 
} 

From the preceding class, the GetItems() method will yield all the data in the  DataItemList, whose content will be as follows:

public partial class ClubMember : IDataSource 
{ 
  List<DataItem> DataItemList = new List<DataItem>() 
  { 
    newDataItem{ 
      Name ="Dorian Villarreal", 
      Gender ="Male"}, 
    newDataItem{ 
      Name ="Olivia Bradley", 
      Gender ="Female"}, 
    newDataItem{ 
      Name ="Jocelyn Garrison", 
      Gender ="Female"}, 
    newDataItem{ 
      Name ="Connor Hopkins", 
      Gender ="Male"}, 
    newDataItem{ 
      Name ="Rose Moore", 
      Gender ="Female"}, 
    newDataItem{ 
      Name ="Conner Avery", 
      Gender ="Male"}, 
    newDataItem{ 
      Name ="Lexie Irwin", 
      Gender ="Female"}, 
    newDataItem{ 
      Name ="Bobby Armstrong", 
      Gender ="Male"}, 
    newDataItem{ 
      Name ="Stanley Wilson", 
      Gender ="Male"}, 
    newDataItem{ 
      Name ="Chloe Steele", 
      Gender ="Female"} 
  }; 
} 

There are ten DataItem classes in the DataItemList. We can display all the items in the DataItemList with the help of the GetItems() method, as follows:

public class Program 
{ 
static void Main(string[] args) 
  { 
    ClubMember cm = new ClubMember(); 
    foreach (var item in cm.GetItems()) 
    { 
      Console.WriteLine( 
        "Name: {0}	Gender: {1}", 
          item.Name, 
            item.Gender); 
    } 
  } 
} 

As we can can see in the preceding code, since we have inherited the ClubMember class to the IDataSource interface and have implemented the GetItems() method, the instance of ClubMember, which is cm, can invoke the GetItems() method. The output will be like what is shown in the following screenshot when we run the project:

Extending the interface

Now, if we want to add the method to the interface without having to modify it, we can create a method extension to the interface. Consider that we are going to add the GetItemsByGender() method to the IDataSource interface; we can create the extension method as follows:

namespaceExtendingInterface 
{ 
  public static class IDataSourceExtension 
  { 
    public static IEnumerable<DataItem>
      GetItemsByGender(thisIDataSourcesrc,string gender) 
    { 
      foreach (DataItem item in src.GetItems()) 
      { 
        if (item.Gender == gender) 
          yield return item; 
      } 
    } 
  } 
} 

By creating the preceding extension method, the instance of the ClubMember class now has a method named GetItemsByGender(). We can use this extension method in the same way as we use the method class, as follows:

public class Program 
{ 
  static void Main(string[] args) 
  { 
    ClubMember cm = new ClubMember(); 
    foreach (var item in cm.GetItemsByGender("Female")) 
    { 
      Console.WriteLine( 
        "Name: {0}	Gender: {1}", 
        item.Name, 
        item.Gender); 
    } 
  } 
} 

The GetItemsByGender() method will return the IEnumerable interface of the selected gender of DataItemList. Since we only need to get all female members in the list, the output will be as follows:

Extending the interface

We can now extend the method in the interface, and there's no need to implement the method in the inherited class since it has been done in the extension method definition.

Extending the collection

In our previous discussion, we discovered that we apply the IEnumerable interface in order to collect all the data we need. We can also extend the IEnumerable interface, which is a collection type, so that we can add a method in an instance of a collection type.

The following is the code in the ExtendingCollection.csproj project and we still use DataItem.cs and IDataSource.cs, which we use in the ExtendingInterface.csproj project. Let's take a look at the following code:

public static partial class IDataSourceCollectionExtension 
{ 
  public static IEnumerable<DataItem>
    GetAllItemsByGender_IEnum(thisIEnumerablesrc,string gender) 
  { 
    var items = new List<DataItem>(); 
    foreach (var s in src) 
    { 
      var refDataSource = s as IDataSource; 
      if (refDataSource != null) 
      { 
        items.AddRange(refDataSource.GetItemsByGender(gender)); 
       } 
    } 
    return items; 
  } 
} 

The preceding code is the extension method for the IEnumerable type. To prevent the occurrence of an error, we have to cast the type of all sources' items using the following code snippet:

var refDataSource = s as IDataSource; 

We can also extend the IEnumerable<T> type, as follows:

public static partial class IDataSourceCollectionExtension 
{ 
  public static IEnumerable<DataItem> 
  GetAllItemsByGender_IEnumTemplate
    (thisIEnumerable<IDataSource> src, string gender) 
  { 
    return src.SelectMany(x =>x.GetItemsByGender(gender)); 
  } 
} 

Using the preceding method, we can extend the IEnumerable<T> type to have a method named GetAllItemsByGender_IEnumTemplate(), which is used to get the items by a specific gender.

Now, we are ready to invoke these two extension methods. However, before we call them, let's create the following two classes, named ClubMember1 and ClubMember2:

public class ClubMember1 : IDataSource 
{ 
  public IEnumerable<DataItem> GetItems() 
  { 
    return new List<DataItem> 
    { 
      newDataItem{ 
        Name ="Dorian Villarreal", 
        Gender ="Male"}, 
      newDataItem{ 
        Name ="Olivia Bradley", 
        Gender ="Female"}, 
      newDataItem{ 
        Name ="Jocelyn Garrison", 
        Gender ="Female"}, 
      newDataItem{ 
        Name ="Connor Hopkins", 
        Gender ="Male"}, 
      newDataItem{ 
        Name ="Rose Moore", 
        Gender ="Female"} 
    }; 
  } 
} 
public class ClubMember2 : IDataSource 
{ 
  public IEnumerable<DataItem> GetItems() 
  { 
    return new List<DataItem> 
    { 
      newDataItem{ 
        Name ="Conner Avery", 
        Gender ="Male"}, 
      newDataItem{ 
        Name ="Lexie Irwin", 
        Gender ="Female"}, 
      newDataItem{ 
        Name ="Bobby Armstrong", 
        Gender ="Male"}, 
      newDataItem{ 
        Name ="Stanley Wilson", 
        Gender ="Male"}, 
      newDataItem{ 
        Name ="Chloe Steele", 
        Gender ="Female"} 
    }; 
  } 
} 

Now, we are going to invoke the GetAllItemsByGender_IEnum() and GetAllItemsByGender_IEnumTemplate() extension methods. The code will be as follows:

public class Program 
{ 
  static void Main(string[] args) 
  { 
    var sources = new IDataSource[] 
    { 
      new ClubMember1(), 
      new ClubMember2() 
    }; 
    var items = sources.GetAllItemsByGender_IEnum("Female"); 
    Console.WriteLine("Invoking GetAllItemsByGender_IEnum()"); 
    foreach (var item in items) 
    { 
      Console.WriteLine( 
        "Name: {0}	Gender: {1}", 
        item.Name, 
        item.Gender); 
    } 
  } 
} 

From the preceding code, first we create a sources variable containing the array of IDataSource. We get the data for sources from the ClubMember1 and ClubMember2 classes. Since the source is a collection of IDataSource, the GetAllItemsByGender_IEnum() method can be applied to it. If we run the preceding Main() method, the following output will be displayed on the console:

Extending the collection

We have successfully invoked the GetAllItemsByGender_IEnum() extension method. Now, let's try to invoke the GetAllItemsByGender_IEnumTemplate extension method using the following code:

public class Program 
{ 
  static void Main(string[] args) 
  { 
    var sources = new List<IDataSource> 
    { 
      new ClubMember1(), 
      new ClubMember2() 
    }; 
    var items = 
      sources.GetAllItemsByGender_IEnumTemplate("Female"); 
    Console.WriteLine(
      "Invoking GetAllItemsByGender_IEnumTemplate()"); 
    foreach (var item in items) 
    { 
      Console.WriteLine("Name: {0}	Gender: {1}", 
        item.Name,item.Gender); 
    } 
  } 
} 

We declare the sources variable in the yet-to-be-displayed code, in the same way as we declared it in the previous Main() method. Also, we can apply the GetAllItemsByGender_IEnumTemplate() extension method to the source variable. The output will be as follows if we run the preceding code:

Extending the collection

By comparing the two images of the output, we can see that there's no difference between them, although they extend the different collection types.

Extending an object

Not only can we extend an interface and a collection, we can actually extend an object as well, which means that we can extend everything. To discuss this, let's take a look at the following code, which we can find in the ExtendingObject.csproj project:

public static class ObjectExtension 
{ 
  public static void WriteToConsole(this object o,    stringobjectName) 
  { 
    Console.WriteLine(
      String.Format(
        "{0}: {1}
",
        objectName,
        o.ToString())); 
  } 
} 

We have a method extension named WriteToConsole(), which can be applied to all objects in C# since it extends the Object class. To use it, we can apply it to various objects, as shown in the following code:

public class Program 
{ 
  static void Main(string[] args) 
  { 
    var obj1 = UInt64.MaxValue; 
    obj1.WriteToConsole(nameof(obj1)); 
    var obj2 = new DateTime(2016, 1, 1); 
    obj2.WriteToConsole(nameof(obj2)); 
    var obj3 = new DataItem 
    { 
      Name = "Marcos Raymond", 
      Gender = "Male" 
    }; 
    obj3.WriteToConsole(nameof(obj3)); 
    IEnumerable<IDataSource> obj4 =new List<IDataSource> 
    { 
      new ClubMember1(), 
      new ClubMember2() 
    }; 
    obj4.WriteToConsole(nameof(obj4)); 
  } 
} 

Before we dissect the preceding code, let's run this Main() method, and we will get the following output on the console:

Extending an object

From the preceding code, we can see that all objects that are UInt64, DateTime, DataItem, and IEnumerable<IDataSource> can invoke the WriteToConsole() extension method that we declare use the this object as an argument.

Tip

Creating an extension method in the object type causes all types in the framework to be able to access the method. We have to ensure that the implementation of the method can be applied to the different types supported by the framework.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset