Click here to Skip to main content
15,879,474 members
Articles / Programming Languages / C#

Enhancing Your Custom Classes

Rate me:
Please Sign up or sign in to vote.
4.83/5 (14 votes)
17 May 2012CPOL8 min read 19K   22   13
You can enhance the value of your custom classes through overrides, creating partials, and explicit type conversion.

Introduction

Microsoft wants you to create your own business and other classes in your projects, and they want you to essentially be able to create behaviors and properties on your classes that mimic the built-in .NET Framework classes. 

If you think about it, the classes available for your use in the .NET base class libraries are extremely versatile and provide copious amounts of behavior and properties for your consumption. Your classes should be that way, too. Most developers create a class that does minimally what the class requires at the time of initial development. With a little forethought and with the realization that your classes will probably be called upon to do more in the future, you should consider adding things to your classes that make them more useful. There are many, many ways to enhance your classes. Three ways: overrides, partials, and explicit type conversion will be discussed here.

Enhancing Your Classes 

Overrides

This is the simplest enhancement. There are specific, defined things about a class that you can "override", or substitute with your code what otherwise would be the built-in default behaviors of the class. Here is a widely known one:

C#
public override String ToString()
{
    return [custom string formation here]
}

In general, every custom class you create should have an overridden ToString() method. The result of the ToString() override will be seen in the debugger hover-over representation of a variable at runtime. If you have need to print out the state of objects to some form of result file, or other document, ToString() is the perfect place to organize your representation of the class. Suppose your class has three string properties called "Prop1", "Prop2", "Prop3". You could override the ToString() as follows:

C#
public override String ToString()
{
    StringBuilder sb = new StringBuilder();
    sb.AppendLine(this.Prop1);
    sb.AppendLine(this.Prop2);
    sb.AppendLine(this.Prop3);

    return sb.ToString();
}

Your class will be represented as three distinct lines of strings. Of course you can get more elaborate and detailed in your override. Again, it is a good idea to override the ToString() method for every class you create.  Once done, you must consider overriding the GetHashCode() method at the same  time.

Another vital override to code for each class is the "Equals" override. This method compares two instances of a given class and returns a boolean value whether they're the same or not. It's up to the individual developer to determine what would make one instance equal to another--only the developer can possibly know.

For example, you have a class with two properties: a String called "ContactName" and a List<string> called "PhoneList".

C#
public class PersonContacts
{
    public String ContactName {  get;  set;  }
    public List<string> PhoneList  {  get;  set;  }
}

You as the developer has sole discretion to decide what would make an instance of PersonContacts equal to another. You decide in this case that one instance of PersonContacts is equal to another instance of PersonContacts if the following is true:

  • The ContactName for each instance is the same
  • The list of phone numbers is the same length and includes the same contacts

You now go about overriding the Equals method:

C#
public override bool Equals(PersonContacts instance1, PersonContacts instance2)
{
    return ((instance1.ContactName == instance2.ContactName) &&
            (instance1.PhoneList.SequenceEquals(instance2.PhoneList));
}

The above implementation uses a simple string compare for ContactName and a LINQ extension for the SequenceEquals comparison (so you must have a reference to System.Linq in your 'using' section. There are a few other things that you should override at this point:

C#
public override bool !=(PersonContacts instance1, PersonContacts instance2)
{
    return (!instance1.Equals(instance2));
}

Likewise you can see how to override the "==" method. As a final note, you may not have yet had much reason for these overrides in your code. Most likely there have been occasions where these overrides could have been helpful but that wasn't immediately obvious from the code. Comparing the equality of two instances of a class is a fairly common need. Having overridden operators such as != is a bonus.

One area where I find the Equals and other comparison operators are helpful is the area of unit testing. A typical unit test instantiates multiple objects (one is the "expected" object which you define in your code, and the other is the "actual" object derived from the running code). These unit test methods usually compare the expected to the actual object and do assertions on those comparisons. In these cases it is indispensable to have good overridden Equals and other comparison operator overrides.

Partial Classes

Partial classes arose from Microsoft as a way to split the definition of a single class among multiple source files. For instance, you could have two source files, one named "source1.cs" and the second "source2.cs". Each source file could define a class called "class1". Each has properties, fields, methods, constants, etc...for the "class1" definition. What is key to know is that at compile time, the C# compiler combines these source files in memory into a single "class1" definition. class1 thus has the data and behaviors of all the members in its definition from both source files.

C#
//source file "source1.cs"  (this source file is auto-generated by a tool)
namespace some_name_here
public partial class Class1
{
    public void method1() {    }
}

namespace some_name_here
//source file "source2.cs"

public partial class Class1
{
    public void method2()  {    }
}

Note that they must be both in the same namespace or else the compiler will treat them as distinct classes.

C#
//...the compiler builds this:

namespace some_name_here
public class Class1
{
    public void method1()  {    }
    public void method2()  {    }
}

Why could this be useful? 

Increasing amounts of code are auto-generated these days. Typical examples are classes generated from XSD schema files, or those generated as proxies of WCF services using the "svcutil.exe" utility. Auto-generation always creates partial classes, not closed ones. This is for a very good reason: the developer using the class may want to extend the functionality of the class, without having to re-incorporate that new functionality into the new class generated by another auto-generation exercise.

In other words, it's a way for you to add new functionality to a class without losing all that work every time the partial class is re-generated from some utility application or from within Visual Studio by adding a reference to a service. For example, a WCF service is published and the client developer creates a proxy using the "svcutil" tool. Then, he wants to "wrap" some of the interface methods with methods of his own. He should create a new partial class in a different source file, and it should have a class definition of the exact same name and namespace of the partial class in the generated proxy. At compile time, the class definitions from both source files are combined into a single usable class.

Now, say the WCF signatures change and a new proxy has to be created. All the effort put into the wrapper methods is not lost--the developer's own partial class is still there. The regenerated class is reintroduced to the class hierarchy and the developer need not rewrite the extended code he placed in his partial class. Full functionality is preserved.

Of course, if something in the newly generated proxy class has changed something relevant to the developer's own partial class, changes will be required in that class. The point is that the majority of the functionality is preserved between code regenerations. Good candidates for inclusion in your partial class are the "overrides" mentioned in the previous section. Partial classes are a good place to override ToString(), Equals, and others. Other candidates for inclusion are "wrapper" methods of methods in the auto-generated file.

Explicit Type Conversion 

Very few application developers ever take advantage of this feature. Just as you can do this:

C#
int myValue = (int)GetSomeValue(); 

...you should be able to do:

C#
MyCustomClass myObj = (MyCustomClass)GetSomeObject();

What's taking place here is explicit type conversion. We're purposefully indicating that we have an object of one type but want to use it to create an object of another type. This is done all the time with the underlying C# base class libraries but more rarely is it done with custom classes that we as developers write. How to do this for our classes?

What you need is a custom object converter on 'MyCustomClass'. That converter method takes an argument of another type of object, and returns an instance of 'MyCustomClass'. Here's an example:

C#
//...this method is located in the 'MyCustomClass' class definition...
//or in a partial class of the same namespace/name...
public static explicit operator MyCustomClass(OtherClass obj)
{
    MyCustomClass myObj = new MyCustomClass();
    myObj.Property1 = obj.PropertyX;
    .....
    return myObj;
}

Note the syntax of the method...it's not immediately obvious what's going on. Your custom conversion method must be public. It must be static. It must be defined with the keywords "explicit" (for obvious reasons) and the keyword "operator". Defined as such, it will be available to provide explicit type conversion in your application. Think about custom type conversions when you're developing. When you need an object of some custom type and you already have a majority of the property values in some other type, it makes sense to create a custom converter instead of writing a bunch of property-value-setting lines of code and sticking it somewhere not obvious...like in that dreaded 'Utils' class.

Often a data provider like Entity Framework will deliver an object to you hydrated with data from a database and located in some entity object. But you have a slightly different internal object hierarchy for your business classes. This is a time when a custom converter might be helpful. Say your populated Entity Framework object was of type "EFType", and your business class was of type "MyBizClass". You now want to hydrate an instance of MyBizClass with data from an EFType object. Write a conversion method.

C#
public static explicit operator MyBizClass(EFType efObj)
{
    MyBizClass myObj = new MyBizClass();
    myObj.Property1 = efObj.PropertyX;
    myObj.Property2 = efObj.PropertyY;
    return myObj;
}

Now, instead of scattering property-to-property copying lines in some unrelated or near-related object, you can focus the code in the conversion method and use it in your main codebase. At runtime you obtain an EFType object called "efObj", populated with data, and now you need to create a MyBizClass object:

C#
MyBizClass newObj = (MyBizClass)efObj;
//...or even better...
MyBizClass newObj = efObj as MyBizClass;

It's as straightforward as that! Your object has more functionality now--the responsibility for hydrating it is not scattered in code in other classes. The code reads more naturally now because the type conversion is done exactly like other .NET conversions are done. Your class should be no different from a respected .NET Framework class.

History

  • Uploaded: 5/15/2012.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
I pursued an education in computer science at the University of California at Hayward. I developed a fascination with Borland Turbo Pascal and C++. Then came 'C' and Fortran. Eventually the academics led me to devour Java. My first software development job forced me to rapidly do Visual Basic 6.0. The software career progressed and I found myself consulting to various companies such as Visa, nVidia, Wells Fargo Bank, and others. Along the way I completed work and earned an M.S. degree in, of all things, computer science. I now find myself back in New England, writing C# applications for a medium size company.

A.S. Computer Science, Chabot College,Hayward CA 1995
B.S. Computer Science, University of California 2000
M.S. Computer Science, University of California 2003

Comments and Discussions

 
SuggestionCare to make it unique in Equality Comparer Pin
VallarasuS28-May-12 0:18
VallarasuS28-May-12 0:18 
SuggestionGreat article, but.. Pin
mbowles20123-May-12 3:37
mbowles20123-May-12 3:37 
GeneralRe: Great article, but.. Pin
Tom R Smith23-May-12 5:42
Tom R Smith23-May-12 5:42 
GeneralMy 2 sense Pin
Mike Hankey28-May-12 7:16
mveMike Hankey28-May-12 7:16 
GeneralRe: My 2 sense Pin
Tom R Smith28-May-12 17:49
Tom R Smith28-May-12 17:49 
GeneralRe: My 2 sense Pin
Mike Hankey29-May-12 2:18
mveMike Hankey29-May-12 2:18 
QuestionGood article Pin
Volynsky Alex17-May-12 1:58
professionalVolynsky Alex17-May-12 1:58 
GeneralMy vote of 4 Pin
cjb11016-May-12 22:18
cjb11016-May-12 22:18 
GeneralRe: My vote of 4 Pin
Tom R Smith17-May-12 1:52
Tom R Smith17-May-12 1:52 
GeneralMy vote of 3 Pin
User 113800016-May-12 16:06
User 113800016-May-12 16:06 
GeneralRe: My vote of 3 Pin
Tom R Smith17-May-12 1:58
Tom R Smith17-May-12 1:58 
GeneralMy vote of 5 Pin
JWhattam16-May-12 13:40
JWhattam16-May-12 13:40 
GeneralMy vote of 5 Pin
  Forogar  16-May-12 8:25
professional  Forogar  16-May-12 8:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.