How To Create A New .NET Class
Java Programming For The Absolute Beginner
|
Subjects > Computers > Software > Programming > Web Application Programming For .NET
|
|
If you moved to C# from VB or another language, you might wonder why the language is so strict in its object-oriented rules. In 'C# Class Design: Coding Effective Classes', you'll learn that in .NET everything is a type and you only can use methods on these types. You must change the way you think about solving problems. Programming used to involve accepting a data type and later checking to see if it is of a certain form, and then have other methods to manipulate that type if you need to.
Now, if you have specific data, in a specific format, it makes sense to create a new type to handle that data and just have your methods/class work on that type. C# additionally allows you to define operator overloads so that once defined, your type can behave in much the same way as you would expect if it were a number or a string.
In the following example program, a new class is created with a few operator overloads in it to show how to use it like a normal type. This is a simple type that represents suits in a pack of cards. It is used to represent what suit is trumps in a Wist game, and so I want to define a plus operator on it that cycles it to the next defined suit on each deal. As an example, the default order will be Hearts, Clubs, Diamonds, Spades. So, on startup a variable holding this value will hold "Hearts". Upon using the addition operator, it becomes Clubs, then Diamonds, then Spades, and then back to Hearts again.
Okay, let's start with the class definition:
using System;
class Suit
{
private const string HEARTS = "Hearts";
private const string CLUBS = "Clubs";
private const string DIAMONDS = "Diamonds";
private const string SPADES = "Spades";
The strings are defined as constants for ease of use.
public static readonly Suit Hearts = new Suit(HEARTS); public static readonly Suit Clubs = new Suit(CLUBS); public static readonly Suit Diamonds = new Suit(DIAMONDS); public static readonly Suit Spades = new Suit(SPADES);
Now I have defined a few fields that permit me to define the 4 different states this class can have (the four suits).
private string[] currentSuit; int index=0;
The above are the private fields that will store the state. Now the constructors:
public Suit(Suit position1, Suit position2, Suit position3, Suit position4)
{
if(position1.Equals(position2)|| position1.Equals(position3)
|| position1.Equals(position4) || position2.Equals(position3)
|| position2.Equals(position4) || position3.Equals(position4))
throw new InvalidOperationException?Create("Must pass 4 distinct values to the constructor");
this.currentSuit = new string[] {position1.ToString?Create(), position2.ToString?Create(),
position3.ToString?Create(), position4.ToString?Create()};
}
The above just specifies the order of the suit, and that for the order of suits, no suits are repeated.
private Suit(string suit)
{
this.currentSuit = new string[] { suit };
}
public Suit() : this(Suit.Hearts, Suit.Clubs, Suit.Diamonds, Suit.Spades)
{
}
The above private constructor is only accessed from the constants above, and the public constructor just defines the default order. The class, is almost defined, we'll just specify the ToString?Create(), Equals(), and GetHashCode?Create() operators.
public override string ToString?Create()
{
return this.currentSuit[index];
}
public bool Equals(string suit)
{
if(suit==HEARTS || suit==CLUBS || suit==DIAMONDS || suit==SPADES)
return(this.GetHashCode?Create()==suit.GetHashCode?Create());
else
return false;
}
public bool Equals(Suit suit)
{
return(this.GetHashCode?Create()==suit.GetHashCode?Create());
}
public override int GetHashCode?Create()
{
return this.currentSuit[index].GetHashCode?Create();
}
Hopefully, the above is quite straightforward. Only one more method to add, and that is the static Parse() method used for parsing a string argument into a Suit class:
public static Suit Parse(string suit) {
if(suit==null)
throw new ArgumentNullException?Create("suit");
suit = suit.Trim().ToUpper?Create();
if(suit=="HEARTS")
return Suit.Hearts;
else if(suit=="CLUBS")
return Suit.Clubs;
else if(suit=="DIAMONDS")
return Suit.Diamonds;
else if(suit=="SPADES")
return Suit.Spades;
else
throw new FormatException?Create("Value parsed is not one Hearts, Clubs, Diamonds, or Spades");
}
|
|
As you can see, we now have a useful type that behaves as expected and so I can pass a Suit type to the methods in my application and instantly know what is going on. However, I said that I wanted to override the ++ operator to allow me to cycle through the suits. Look below to see how this is done:
public static Suit operator ++(Suit suit) {
if(suit.currentSuit==null || suit.currentSuit.Length==1) return suit; suit.index++; if(suit.index>3) suit.index=0; return suit;}
What this does is simply return the same object an order hasn't been specified (it is one of the readonly fields), or increase the index by one (returning it to 0 if it is higher than 3) and returning itself. This isn't very exciting. You could define a method to do this. However, you can also define implicit and explicit casting operators. Look below:
public static implicit operator Suit(string suit)
{
return Suit.Parse(suit);
}
public static implicit operator string(Suit suit)
{
return suit.ToString?Create();
}
With this, I can define a new type by saying: Suit suit = hearts; and I can also use a line like Console.WriteLine?Create(suit); to return the name of the suit. These are very useful to help make your type behave like a value type. You can also override the equality operators so that you can compare this in a more intuitive way. See below:
public static bool operator ==(Suit suit1, Suit suit2)
{
return suit1.Equals(suit2);
}
public static bool operator !=(Suit suit1, Suit suit2)
{
return !suit1.Equals(suit2);
}
Whenever you define an equals operator, you should always define a not equals one. I just use the new methods I defined to do this. We can also do the same if we are comparing this with string objects:
public static bool operator ==(Suit suit1, string suit2)
{
return suit1.Equals(suit2);
}
public static bool operator !=(Suit suit1, string suit2)
{
return !suit1.Equals(suit2);
}
public static bool operator ==(string suit2, Suit suit1)
{
return suit1.Equals(suit2);
}
public static bool operator !=(string suit2, Suit suit1)
{
return !suit1.Equals(suit2);
}
}
You have to include the cases where strings are before and after the == sign. Now you have a type that you can compare and use like a string, but that will always have predictable values.
You can learn so much more about how to use types effectively in your C# code in the C# Class Design Hanbook from Wrox Press. C# Class Design - Coding Effective Classes'
See also 'Professional C# (2nd Edition)' and 'C# Text Manipulation Handbook' for more information
|
|
Some other articles here about programming web applications for .NET:
Could not find Programming/BottomAd1?Create
|
Interested in All The Apostles Of The Bible - I. The Prayerful, Purposeful, And Particular Choice Of The Twelve?