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:
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:
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
".
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:
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:
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.
namespace some_name_here
public partial class Class1
{
public void method1() { }
}
namespace some_name_here
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.
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:
int myValue = (int)GetSomeValue();
...you should be able to do:
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:
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.
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:
MyBizClass newObj = (MyBizClass)efObj;
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
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