in

Dé specialist in .NET trainingen en consultancy

Thomas Huijer

oktober 2008 - Posts

  • Versienummering

    Ik had laatst een gesprek met een klant die overstapten van klassiek ASP naar ASP.NET. Op een gegeven moment kwam het gesprek op de verschillende versies van het Framework en C#. Dat gesprek ging ongeveer zo:

     

    Thomas, hoe zit het nou met die versienummering van C#?

    Ehm, van C# of van het .NET framework?

    Hoe bedoel je?

    Nou, de versienummering van C# loopt niet gelijk met die van het .NET Framework. Bijvoorbeeld, in het 3.5 framework zit C# 3.0.

    Aha, maar wat zit er dan in het 3.0 Framework?

    Daar zit C# 2.0 in.

    Ok, en ik durf het bijna niet te vragen, maar welke C# versie zat er dan in het 2.0 Framework?

    Gewoon C# 2.0.

    Ja, ja. Maar in het 1.1 Framework zat daar dan C# 1.1 in?

    Nee, dat dan weer niet. In het 1.1 Framework zat gewoon C# 1.0.

    Natuurlijk...Maar goed, ik zie hier assemblies met als versienummer 1.0.5000.0, dus dat is dan het 1.0 Framework met hopelijk C# 1.0.

    Dat ook weer niet. Het 1.0.5000.0 Framework is het 1.1 Framework. De interne nummering wijkt af van de externe nummering van het Framework. Dus assemblies met 1.0.5000.0 als versienummer zijn onderdeel van het 1.1 Framework waar dus C# 1.0 in zit.

    Je meent het? Er zal wel een soort van logica in zitten. Maar die versies gaan we toch allemaal niet meer gebruiken. We gaan naar C# 3.0, Visual Studio 2008 en het 3.5 Framework. Maar waarom zit er eigenlijk in het 3.0 Framework dan geen C# 3.0?

    Ach, ja, technisch gezien is het 3.0 Framework helemaal geen nieuw framework, maar het 2.0 Framework met een aantal uitbreidingen. Qua taal is er niets veranderd in het 3.0 Framework.

    Maar waarom heette C# 3.0 dan geen C# 3.5 als het toch alleen werkt op het 3.5 Framework?

    Microsoft's wegen zijn soms ondoorgrondelijk. Zeker de wegen van de club die die versienummer verzint.

    Maar blijven ze aan de gang of komt het ooit nog goed?

    Het komt wel goed. Straks in 2010 hebben we C# 4.0 en het 4.0 Framework. Tenminste, zoals het er nu uitziet...

  • Sets in C# using generic extension methods

    Het zal mijn Borland verleden zijn, maar ik vond de implementatie van sets in C# nogal zwak. Delphi heeft daar een mooie oplossing voor.

     

    type
      TColor = ( clRed, clGreen, clBlack );  
      //c# public enum Color (Red, Green, Black)
    
    
      TColors = set of TColor;
    // in c# bestaat dit niet, je kan hooguit het [Flags] attribuut gebruiken.

    In Delphi zijn het dus twee verschillende typen (en terecht: een element is geen verzameling en vice versa!). Maar in C# mag je gaan ANDen en ORen met bitflags. Nogal low-level, en ja, ik snap het allemaal wel, maar leesbaar? Nee, dat absoluut niet.

     

      [Flags]

      public enum TestEnum

      {

        Value1 = 1,

        Value2 = 2

      }

     

    En dat krijg je dus dit soort code om te testen of een element in een verzameling zit. Want dat is toch wat je uiteindelijk doet...

     

          TestEnum v = TestEnum.Value1 | TestEnum.Value2;

     

          if ( ( v & TestEnum.Value1 ) == TestEnum.Value1 )

          {

            //

          }

     

    En ik zou gewoon dit willen:

          TestEnum v = TestEnum.Value1 | TestEnum.Value2;

     

          if ( TestEnum.Value1 in v )

          {

            //

          }

     

    Maar goed, dat zal niet lukken. Tenminste niet zonder toe te treden tot het C#-compiler team bij Microsoft :-). Maar toen bedacht ik me dat een extension method dit misschien wel eens zou kunnen gaan oplossen:

     

      public static class SetHelper

      {

        public static bool In( this System.Enum value, Enum flags )

        {

          Int64 flagInt = ( (IConvertible) flags ).ToInt64( null );

          Int64 valueInt = ( (IConvertible) value ).ToInt64( null );

          return ( flagInt & valueInt ) != 0;

        }

      }

     

    Nu ik toch Delphi aan het promoten ben: extension methods is ontsproten aan het brein van Chuck Jazdzewski (yep, dat was een copy en paste-actie). Ooit lang geleden geimplementeerd als class helpers, en nu sinds het 3.5 Framework ook in .NET. Maar goed, met die extension method kom je al een heel eind.:

     

          TestEnum v = TestEnum.Value1 | TestEnum.Value2;

     

          if ( TestEnum.Value1.In( v ) )

          {

            //

          }

     

     

    En zo heb ik ook de Contains, IsSubsetOf en de IsSupersetOf gemaakt. Maar het werd pas echt grappig toen ik de Union wilde implementeren. Dit is mijn eerste poging:

     

        public static Enum Union( this Enum set1, Enum set2 )

        {

          Int64 set1Value = ( (IConvertible) set1 ).ToInt64( null );

          Int64 set2Value = ( (IConvertible) set2 ).ToInt64( null );

          return (Enum) Enum.ToObject( set1.GetType(), ( set1Value | set2Value ) );

        }

     

     

    Maar dat betekende wel dat ik altijd moest gaat casten. Bovendien kon je niet garanderen dat je twee dezelfde enum typen opgaf als argumenten. Dat moest beter kunnen! Dus maar eens gekeken of ik een generic extension method kon maken. En dat blijkt geen enkel probleem te zijn. LINQ is ook grotendeels opgebouwd uit generic extension methods op interfaces:

     

     

        public static T Union<T>( this T set1, T set2 )

          where T : struct, IConvertible //compiler doesn't allow System.Enum

        {

          Int64 set1Value = ( (IConvertible) set1 ).ToInt64( null );

          Int64 set2Value = ( (IConvertible) set2 ).ToInt64( null );

          return (T) Enum.ToObject( typeof( T ), ( set1Value | set2Value ) );

        }

     

     

        public static T Intersect<T>( this T set1, T set2 )

          where T : struct, IConvertible //compiler doesn't allow System.Enum

        {

          Int64 set1Value = ( (IConvertible) set1 ).ToInt64( null );

          Int64 set2Value = ( (IConvertible) set2 ).ToInt64( null );

          return (T) Enum.ToObject( typeof( T ), ( set1Value & set2Value ) );

        }


    En dan houden we dit soort code over:

          v = TestEnum.Value1;

          v = v.Union<TestEnum>( TestEnum.Value2 );

          MessageBox.Show( TestEnum.Value2.In( v ) ? "Value2 is set in v" : "Value2 is not set in v" );

     

     

     

    Ik ben al redelijk tevreden omdat ik zo een stuk leesbaarder met sets kan omgaan in C#. Iemand nog ideeen hoe we dit nog cleaner kunnen krijgen?