Browse DevX
Sign up for e-mail newsletters from DevX


Build a Generic Range Class with .NET 2.0 : Page 3

Find out how to take advantage of the generics capability introduced with .NET 2.0, which provides an elegant solution to performing range checks within your applications.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

Coding to the Test
For the most part, the current implementation of the interface and class are very simple—sufficient to get the code to compile. But if you run the test right now it should fail. That's the point however! So far, you've created a generic interface called IRange<T> that requires one generic type argument—the type for which you want to create the range. The Range<T> class implements the IRange<T> interface, satisfying the IRange<T> interface argument using the type argument that a client provides upon construction. Take a look at the arguments to the constructor:

public Range(T start, T end) { throw new NotImplementedException(); }

Remember, you don't know what types the clients are going to want to use the Range<T> class for, so you must substitute the generic type parameter (T) wherever you need to use the actual type the range is created for. The constructor tells clients that they must provide a new Range with a start value and an end value. For example to create a IRange<T> of integers you would do the following:

IRange<int> rangeOfIntegers = new Range<int>(20, 30);

Note that when you construct a Range<int> that you can pass only integer values to the constructor. Any method in the class that accepts or returns T is now strongly typed to the type provided to the constructor as a generic argument (in this case integers).

Let's get back to making the test pass. Update the code in the Range class to match the code below. After doing that, it should pass the test.

public class Range<T> : IRange<T> { private readonly T start; private readonly T end; public Range(T start, T end) { this.start = start; this.end = end; } public T Start { get { return this.start; } } public T End { get { return this.end; } } }

Now you can add a second test for the integer range:

Figure 1. NUnit Test Failure: The figure shows the NUnit failure output caused by running the new Range test.

[Test] public void ShouldCreateRangeWithStartAndEndAssignedCorrectly() { IRange<int> rangeOfIntegers = new Range<int>(30, 20); Assert.AreEqual(20,rangeOfIntegers.Start); Assert.AreEqual(30,rangeOfIntegers.End); }

This test ensures that the Range will assign the correct values to the start and end of the range, even if the client of the Range provides it with mixed up values upon construction. Figure 1 shows the NUnit failure caused by the running the new test:

The test fails because the Range does not yet implement the logic to correctly rearrange and assign values passed to the constructor. You would logically think that you could implement the assignment code using the following logic:

public Range(T start, T end) { if (start <= end) { this.start = start; this.end = end; } else { this.start = end; this.end = start; } }

However, if you write the above code you will quickly see that it does not compile but gives you the following error message:

Figure 2. Non-specific Generic Type Methods: The four methods shown are the only methods available to all generic types.

"Cannot apply operator '<=' to operands of type 'T' and 'T' ".

Why are you getting this error? Remember, you are coding your Range class so that it can work with any type. This actually raises a problem. How can you be sure that all types that would want to consume your class actually provide a <= operator. The answer is: You can't. This puts you in an interesting predicament. Figure 2 shows the methods at your disposal when accessing arguments of type T—and that's it. In other words, you have nothing more to work with than the same methods available to all descendants of Object (which is every class in the framework).

Thanks for your registration, follow us on our social networks to keep up-to-date