Solution Builder - Channel Insider
Empowering the next generation Channel
 

Sponsored Links
  • Get up and running in as quickly as 30 days with BI. Learn how today.
  • FREE Securing Smartphones & Tablets for Dummies Book from Sophos
  • 5 New Technologies That Will Change Enterprise ITAdvertisement
  • Build an IT Infrastructure That Delivers the Future

  •  

    Generic Methods in Visual Studio 2005

    in Solution Builder



    Article Rating:starstarstarstarstar / 1
    Article Views: 4080

    You might think generics and templates are very hard to use, but they aren't, not really. Paul Kimmel shows you how to define generic methods and explains their implementation in .NET 2.0, using C# and VB examples. And he makes it simple. Honest.

    Rate This Article:
    Add This Article To:

    Some things are good in their own time. Bell bottoms were deemed to be good in the 1970s, but today, after a short revival, they are considered goofy. In contrast, blue jeans have always been deemed to be good, whether you choose Wranglers or Levi's. The same thing is true of code idioms: some idioms are always good, and some are good in their own time.

    For example, templates are deemed good, but for VB.NET programmers their time hadn't come until now. Called generics in .NET, Microsoft is introducing generics into languages like C# and VB.NET. (Microsoft could have introduced generics into VB earlier, but the audience probably just wasn't ready.)

    Unlike bell-bottoms, generics have always been useful, because they help eliminate the need to write the same algorithm over and over when the only thing that changes is data type — think Stack algorithm — and they help eliminate ugly conditional type checking and coercion. Now, C# and VB.NET support generics.

    Historically, generics and templates have been perceived as very hard to use, but they aren't, not really. In this article, I demonstrate how to define generic methods and tell you about their implementation in .NET 2.0.

    The Problems Generics Solve

    .NET has a common type system. This means all objects have, at their absolute root, a class named object, even simple types like int/Integer. In a technical sense, this means that you could write an abstract data type, Stack (although one already exists), and store objects in it. Since Stack already exists, Listing 1 offers a simple example that illustrates a problem with stacks based on a common subtype.

    Listing 1: Problems with common sub-types and data structures.

    Imports System.Collections
    
    Module Module1
    
        Sub Main()
          Dim s As Stack = New Stack
          s.Push(New Gun)
          s.Push(New Camera)
          
          While (s.Count > 0)
            Dim o As Object = s.Pop
            o.Aim()
            o.Shoot()
          End While
    
          Console.ReadLine()
    
        End Sub
    
    End Module
    
    Public Class Gun
      Public Sub Aim()
    
      End Sub
    
      Public Sub Shoot()
        Console.WriteLine("You're dead")
      End Sub
    End Class
    
    Public Class Camera
      Public Sub Aim()
    
      End Sub
    
      Public Sub Shoot()
        Console.WriteLine("You're a supermodel")
      End Sub
    End Class

    From the example in Listing 1, you see that we can put any type of object in the basic Stack class. The result is that, without a lot of type checking and type coercion, we can't be sure that some unexpected type hasn't found its way in the stack, resulting in an unanticipated runtime error, or worse, supermodels with holes in them.

    One solution is to write a type-specific Stack for each kind of object we want to store using a stack. Generics eliminate the need to write type-specific stacks (or anything else), and eliminate the need to for type checking and type coercion. Well, slightly more precisely, Generics eliminate the need to us to write type-specific algorithms, which in turn also eliminates the need to write type-checking and type-coercing code.

    Using a Generic Stack

    The problem is easily mitigated by using generics. In our supermodel-homicide example, we can use the System.Collections.Generic.Stack class, which would prevent us from shooting the supermodel with the Gun, because Gun wouldn't be permitted in a stack of cameras (see Listing 2).

    Listing 2: A revision of listing 1 that uses a Generic stack ensuring type homogeneity. Imports System.Collections.Generic.

    Module Module1
    
        Sub Main()
          'Dim s As Stack = New Stack
          Dim s As Stack(Of Camera) = New Stack(Of Camera)
          's.Push(New Gun)
          s.Push(New Camera)
          
          While (s.Count > 0)
            Dim o As Object = s.Pop
            o.Aim()
            o.Shoot()
          End While
    
          Console.ReadLine()
    
        End Sub
    
    End Module
    
    Public Class Gun
      Public Sub Aim()
    
      End Sub
    
      Public Sub Shoot()
        Console.WriteLine("You're dead")
      End Sub
    End Class
    
    Public Class Camera
      Public Sub Aim()
    
      End Sub
    
      Public Sub Shoot()
        Console.WriteLine("You're a supermodel")
      End Sub
    End Class

    In the revised example, trying to put Gun in the Stack(Of Camera) would report a compile-time error, which effectively means we wouldn't be shooting supermodels with guns, even by accident.

    Generic methods can be written independently, or as part of a generic class. They are the first step toward creating type agnostic solutions. In the rest of this article, we focus on writing generic methods.

    Writing a generic class is easy once you know how to write generic methods. Simply wrap the generic method in a class, and add the notation to the class header for the permits specifying the generic type when the class is declared and instantiated.

    Defining Generic Methods

    A generic method is a method where one or more parameter types are unspecified in the method header. It is up to the method caller to provide the data type at each point of invocation. In C# and in VB.NET, this means we use a non-data type for at least one parameter type; T is used by convention. It also means that we use an extended notation specific to the language. In C#, we add a <T> after the method name before the parentheses, and in VB.NET we use an (Of T) after the method name and before the parameter parentheses. (Listing 3 provides a VB.NET example; Listing 4 is a C# example.)

    It is worth noting that we can have multiple generic types. Return types and local parameters can be defined as the generic type, too.

    Listing 3: A generic method body for Pop in VB.NET.

    Public Function Pop(Of T)() As T
    
    End Function
    
    Listing 4: A generic method body for Pop in C#.
    public T Pop<T>()
    {
    }

    The just-in-time compiler for .NET is responsible for converting a method call against these generic methods into a type-specific method instance based on the type provided at invocation. For example, to use the stand-alone C# Pop method to Pop a Camera instance, we would call Pop this way:

    Pop<Camera>

    Adding Generic Method Constraints

    Generic methods in .NET support optional method constraints that limit the kinds of data types with which a particular method can be invoked. In C#, this is accomplished by using a

    WHERE T : is_kind

    predicate at the end of the method header and an

    (Of T As is_kind)

    after the Of T predicate, when defining a generic constraint for VB.NET. For example, we could define a generic method named Swap ,and limit possible types to non-nullable structures like int/Integer but not string/String by using a generic constraint. Listing 5 shows Swap in VB.NET, and Listin6 shows Swap in C#. Both have the constraint that limits the generic method to struct/Structure.

    Listing 5: Demonstrates a generic method with a structure constraint in VB.NET.

    Module Module1
    
        Sub Main()
          Dim I As Integer = 5
          Dim J As Integer = 7
          Swap(Of Integer)(I, J)
          Console.WriteLine("I = " & I)
          Console.WriteLine("J = " & J)
          Console.ReadLine()
    
        End Sub
    
    
        Public Sub Swap(Of T As Structure)(ByRef a As T, ByRef b As T)
          Dim temp As T = a
          a = b
          b = temp
        End Sub
    
    End Module

    After Swap(Of Integer)(I, J) is called, I is 7 and J is 5.

    Listing 6: Swap implemented as a C# method with the struct constraint. This code produces results identical to the code in Listing 5.

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace SwapInCSharp
    {
      class Program
      {
        static void Main(string[] args)
        {
          int i = 5;
          int j = 7;
          Swap(ref i, ref j);
          Console.WriteLine("i = " + i.ToString());
          Console.WriteLine("j = " + j.ToString());
          Console.ReadLine();
        }
    
        public static void Swap(ref T a, ref T b) where T : struct
        {
          T temp = a;
          a = b;
          b = temp;
        }
      }
    }

    The complete list and definition of possible constraints is provided in the .NET help documentation. By definition, generic types can be limited to structures, classes, classes with default constructors, base classes, and specific interfaces.

    Tip: An example of a nullable struct/Structure is the string/String class. The int/Integer is not nullable can be coerced to be nullable by adding a question mark (?) after the data type. As defined in Listings 5 and 6, we couldn't use this version of Swap for string/String types.

    Writing Generic Class Headers

    To create generic classes, all you need to do is have a class with at least one generic member and a means of letting the class consumer specify the actual type of the generic member. This is done in the class header, much the same way it is accomplished in a generic method.

    To define a generic class in VB.NET, add the (Of T) predicate after the class name, and to define a generic class in C# add a <T> after the class name. When you create an instance of the class, simply specify the data type of the generic parameter T. That's all there is to it.

    Summary

    There are a lot of small subtleties in how generic methods are defined and used, but the basic use is simple: convert the data type of parameters and local variables to the named generic type reference, and you are all set. Figuring out when to use generic methods takes some practice; their implementation in .NET is comparatively easy.

    The good news is that you do not need generics every day. You should not let their presence intimidate you, because you don't have to use generics at all. Generics do provide useful benefits without presenting any practical limitation to getting started with Visual Studio 2005 today.

    Paul Kimmel is the founder of Software Conceptions, Inc, founded in 1990, and the co-founder of the Greater Lansing Area .NET Users Group. Look for his upcoming book, UML DeMystified.




    comments dic


     
     
    >>> More Solution Builder Articles          >>> More By Paul Kimmel
     


     



    channel chatter


    HTML PLAIN TEXT

    Keep on top of news for VARs and Resellers with CI's Weekly Newsletter and Alerts.


    [ci] feeds
    XML
    Add Channel News, Product Reviews, Trends and Analysis to your RSS newsreader or My Yahoo!


     


    CHANNEL SPONSORED RESOURCE CENTER
     
     
     
    Start the New Year with business intelligence—it’s a smart move
    Join us on February 1 for an encore rebroadcast at either 5 am or 12 noon EST and discover how business intelligence (BI) supports companies in uncertain business and economic climates. Get expert advice on how to create a strategy that fits your organization's needs and budget and see how quickly it can pay for itself.
    Click Here
     
    Security and Availability Essentials for Running Your Business in the Cloud
    Are you moving to the cloud? Find out what every IT professional should know about security and availability before moving to the cloud. Hear what a security provider’s own CSO has to say.
    Watch Video
    A new algorithm automatically identifies relationships between variables to help reduce researcher prejudice.
    Click HereAdvertisement