Recipe 6.10. Organizing Your Interface ImplementationsProblemYou have a class that implements an interface with many methods. These methods support only the interface functionality and don't necessarily relate well to the other code in your class. You would like to keep the interface implementation code separate from the main class code. SolutionUse partial classes to separate the interface implementation code into a separate file. For example, you have a class called TriValue that takes three decimal values and performs some operations on them, like getting the average, the sum, and the product. This code is currently in a file called TriValue.cs, which contains: public partial class TriValue { decimal first; decimal second; decimal third; public TriValue(decimal val1, decimal val2,decimal val3) { this.first = val1; this.second = val2; this.third = val3; } public TypeCode GetTypeCode() { return TypeCode.Object; } public decimal GetAverage() { return (GetSum() / 3); } public decimal GetSum() { return first + second + third; } public decimal GetProduct() { return first * second * third; } } Now you want to add support for the IConvertible interface to the triValue class so that it can be converted to other data types. We could just add all 16 method implementations to the class definition in TriValue.cs and hide the code using a #region statement. Instead, you can now use the partial keyword on the triValue class and store the IConvertible implementation code in a separate file. Once a class begins to be defined in multiple files, it is important to have a naming convention for those files, so that it is easy to find implementation code and for other developers to understand where to put new code when it is added to this class. We will use the [BaseClass].[Interface].cs naming convention here. This will give you a new file called TriValue.IConvertible.cs that contains the IConvertible interface implementation code, shown in Example 6-6. Example 6-6. Using partial classes to organize your interface implementations
Now you have the interface implemented and your original class definition is still straightforward. For classes that implement many interfaces, this approach will allow for a more tightly organized implementation. DiscussionIt should be noted that there is no Microsoft intermediate language (MSIL) indicator that these are partial classes if you look at your class in Ildasm or Reflector. It will look just like a normal class by the time it gets to MSIL. Intellisense handles the merge as well. Since partial types are a language trick, they cannot span assemblies, as the class needs to be resolved by the compiler. Partial types can be declared in the same file as well as in separate files, but still must be in the same namespace so the compiler can resolve it before generating the MSIL. You can use the partial type support for classes, nested classes, structures, and interfaces, but you cannot have a partial enum definition. Partial types can declare support for different interfaces per partial type. However, single inheritance is still in force and must be the same or omitted from the secondary partial type. You can see that in the Solution the partial triValue class definition in TriValue.cs you created does not specify the inheritance from IConvertible, only the one in TriValue.IConvertible.cs does. The previous TriValue class can be exercised with the following code: class Program { static void Main(string[] args) { TriValue tv = new TriValue(3, 4, 5); Console.WriteLine("Average: {0}",tv.GetAverage()); Console.WriteLine("Sum: {0}", tv.GetSum()); Console.WriteLine("Product: {0}", tv.GetProduct()); Console.WriteLine("Boolean: {0}", Convert.ToBoolean(tv)); Console.WriteLine("Byte: {0}", Convert.ToByte(tv)); Console.WriteLine("Char: {0}", Convert.ToChar(tv)); Console.WriteLine("Decimal: {0}", Convert.ToDecimal(tv)); Console.WriteLine("Double: {0}", Convert.ToDouble(tv)); Console.WriteLine("Int16: {0}", Convert.ToInt16(tv)); Console.WriteLine("Int32: {0}", Convert.ToInt32(tv)); Console.WriteLine("Int64: {0}", Convert.ToInt64(tv)); Console.WriteLine("SByte: {0}", Convert.ToSByte(tv)); Console.WriteLine("Single: {0}", Convert.ToSingle(tv)); Console.WriteLine("String: {0}", Convert.ToString(tv)); Console.WriteLine("Type: {0}", Convert.GetTypeCode(tv)); Console.WriteLine("UInt16: {0}", Convert.ToUInt16(tv)); Console.WriteLine("UInt32: {0}", Convert.ToUInt32(tv)); Console.WriteLine("UInt64: {0}", Convert.ToUInt64(tv)); } } The preceding code produces the following output: Average: 4 Sum: 12 Product: 60 Boolean: True Byte: 4 Char: _ Decimal: 4 Double: 4 Int16: 4 Int32: 4 Int64: 4 SByte: 4 Single: 4 String: (3,4,5) Type: Object UInt16: 4 UInt32: 4 UInt64: 4 See AlsoSee the "Partial Class Definitions" and "partial Keyword" topics in the MSDN documentation. |