Generics: Not Just for Lists

Posted by on in Blogs
It appears that the next version of Delphi, code-named Tiburón, will include support for generic types in the Win32 compiler.  Developers without .NET experience may not be familiar with use cases for generics.  Of course, generics work really well for typed containers, and this is often the first example that people give as a use case.  But they have many other uses as well.  I'll give a few examples; I hope this will help Delphi developers think about how they might want to use the feature in their own code.

Nullable<T>


It's common to have a reference in code to a value read in from a database.  Of course, most databases allow NULL, whereas many scalar types, like integer, do not. So a programmer can either choose to designate a certain scalar value as a "NULL flag," which can cause problems when that value is actually stored in the database, or to have a separate, Boolean reference indicating whether the other variable should, in fact, be treated as NULL.  Neither solution is great. A better way is to build a single structure which stores both the NULL flag and the scalar value.  This is a more elegant way of storing the value retrieved from the database, but it gets tedious and hard to maintain when you have to create such a structure for many different types.  Using a generic Nullable<T>, however, you can use a single type which can store a reference to any value, whether scalar or not.

Mocks


Generics work really well for mock classes used in unit testing.  Briefly, a mock is an object which appears to be of a certain type, but is, in fact, just an imitation of that type.  You use a mock in a unit test when including the actual object would make the unit test dependent on things which are not practical or advisable to include in a unit test, like a web service.  The mock can pretend to be the web service and return data without actually making an Internet connection.

Mocking is one of those things which is really simple to implement for one specific case, and fairly hard to implement for a general case.  You can fairly easily create a mock for one specific type.  It's quite a bit harder to create a mocking framework which can imitate any type. But generics help a lot.

In my unit tests for my ASP.NET MVC web applications, I need to mock the HttpContext, since the version included with the .NET framework polls and a whole bunch of stuff related to web applications which I don't need for the unit test.  So I use the Moq framework and a mocked context which I posted earlier.  In general, to create a mock of a certain type using Moq, you write:

var mockobject = new Mock<TypeToMock>();


After some additional code to specify what values the methods and properties of the "fake" TypeToMock should return, I can then supply the mocked object to some other instance which expects a "real" instance of TypeToMock, and it will be none the wiser:

testInstance.TypeToMockProperty = mockobject.Object;


Now, a full explanation of how to use the Moq framework is outside of the scope of this post; you can read a brief tutorial at the Moq site if you're interested.  But the important point is that mockobject.Object appears to the outside world to be an instance of TypeToMock, even though it isn't, and doesn't carry any of the baggage that the "real" TypeToMock might have.

MVC


Another area where I've found generics to be really helpful is when passing a model to a view in an ASP.NET MVC web application.  MVC View pages have a property called ViewData.Model which returns a model instance that was passed by the controller.  By default, this is of type System.Object, which means that you have to cast it to do anything useful with it.  But by using generics, you can get rid of the casts.  Let's say that the model I want to supply to the view is of type MyModel.  I changed the view declaration from this:

public partial class MyView : ViewPage


...to this:

public partial class MyView : ViewPage<MyModel>


ViewPage<MyModel> is a different type than ViewPage (without the generic type parameter).  MyView is now subclassing a different parent type. In particular, the property ViewData.Model of ViewPage<MyModel> will be of type MyModel instead of type System.Object.  This means that in the code for my View, I can access properties of my model type directly, without any casts, like this:

editLink.HRef = ViewData.Model.EditUrl;


Note that this is not some kind of disguised cast. With a cast, you either sacrifice performance (with a safe/soft cast) or type safety (with an unsafe/hard cast).  With a generic, you don't make either sacrifice.  Type checking is performed at compile time, just like with a property of a non-generic type.

So When Should I Use Generics?


There are some "code smells" which indicate that you should consider using a generic type.  Whenever you find yourself writing similar code over and over again just to change one or two types (e.g., the type-safe lists that most of us have built in Delphi), or whenever you find yourself casting a reference regularly to the same type, you might want to consider using a generic type instead.


Comments

  • Guest
    Lars Fosdal Thursday, 7 August 2008

    I have to admit I am practically drooling over Generics for Win32. Really powerful stuff!

  • Guest
    Jolyon Smith Thursday, 7 August 2008

    On Nullable, you forgot to mention that Variant has a formal Null identity. Of course, Variant has it's own issues, but for completeness... :)

    One problem I see with Nullable is - surely - that the result is not directly usable as a scalar, or is it?

    i.e. given:

    a: Integer;
    b: Nullable;

    could I write:

    a := b
    if a > b then

    etc? Maybe this works transparently in .NET (dunno) but in a Win32 implementation? It would be interesting to find out (and, if it does, how?)


    I also wonder how important Null is to application code generally - typically (ime) a Null in the DB is interpreted by an application as representing some internally defined value - i.e. not set by the user/data, so determined by the system.

    Sometimes, yes, simply to indicate Null or "No Value", but more often (ime) to mean "the system defined default value, whatever that is in this revision or build of the system", which isn't actually null but some definite value.

    So to that extent, the need to carry a Null flag around with a value the whole time is unwarranted if the only time that the null-quality of a value is at the application/DB boundary.

    Of course, ymmv.


    As with anon methods (and a lot of other things), I personally don't think it's possible to identify a set of conditions that prescribe when you *should* use any particular language feature.

    You can only identify those cases where you *could*. But whether you should or not depends on a other factors that will vary from case to case.

    I can see how I might (dare i say "hopefully") use generics in my multicast events implementation. It will require that the implementation allows the use of event signature types as the parameter to a type:

    TMultiCast

    Even if this is possible, whether I should or not is a separate question.

    For one thing, by doing so I will at a stroke deny access to my code to anyone not using Tiburon.

    That's going to be a mighty tough call for anyone creating code that they intend, or hope, will be used by other people.

  • Guest
    Daniel Lehmann Thursday, 7 August 2008

    About Nullables, have a look at what Nick Hodges said about them:

    http://blogs.codegear.com/nickhodges/2008/07/31/39092#comment-19975

    So it should be possible to create our own Nullable Type there will probably no default implementation in the rtl (what a shame). Sounds like everyone will have competing TNullable's then :( And of course without language support "Integer?" or Chrome's "mySomething::SomeMember" they are only half the fun...

  • Please login first in order for you to submit comments
  • Page :
  • 1

Check out more tips and tricks in this development video: