none
GUID nicht in TypeCode? RRS feed

  • Frage

  • Hallo zusammen,

    jetzt habe auch ich mal wieder eine Frage.

    Folgender Code:

    TypeCode tc = Type.GetTypeCode(t.Value.GetType());
    
    if(tc == TypeCode.String || tc == TypeCode.DateTime || t.Value.GetType() == typeof(Guid)) {
      //...more code
    }
    

    Warum ist es nicht möglich den Datentyp per TypeCode auf System.Guid zu prüfen? Wie prüft ihr möglichst performant eine Reihe von Typen ab?

    Vielen Dank und viele Grüße
    Holger M. Rößler


    Kaum macht man es richtig, schon funktioniert es
    Freitag, 1. Juli 2011 21:22

Antworten

  • Hallo Holger,

    TypeCodes hat man nur für die "wichtigeren" Typen reserviert.
    Wichtig insofern als sie von der CLR stellenweise eine spezielle Behandlung erhalten,
    was der Tabelle integrierter Typen (C#-Referenz) entspricht.

    Alle anderen müssen sich mit TypeCode.Object zufrieden geben.
    Und eine Guid erfährt die so nicht (und wäre als  eine Ansammlung von 11 Feldern kaum geeignet - siehe Konstruktor)

    Für einige wenige kannst Du schreiben:

          Type valueType = value.GetType();
          TypeCode typeCode = Type.GetTypeCode(valueType);
    
          // TimeSpan dazu erfunden ;-)
          if (typeCode == TypeCode.String 
            || typeCode == TypeCode.DateTime
            || (typeCode == TypeCode.Object 
              && (valueType == typeof(Guid) 
                || valueType == typeof(TimeSpan))))
            return true;
    
    
    oder (bei einigen mehr) die Sprungtabelle von C# switch nutzen:

          switch (typeCode)
          {
            case TypeCode.String:
            case TypeCode.DateTime:
            // sinniger wenn noch mehrere kämen
              return true;
            case TypeCode.Object:
              if (valueType == typeof(Guid) || valueType == typeof(TimeSpan)) 
                return true;
              return false;
          }
    
    was IMO auch freundlicher zu lesen ist.

    Sind es einige Typen mehr, so kannst Du ein statisches HashSet<Type> anlegen,
    was dann ein O(1) Zugriff über den Hashcode wäre.

    Gruß Elmar


    Samstag, 2. Juli 2011 04:55
    Beantworter

Alle Antworten

  • Hallo Holger,

    TypeCodes hat man nur für die "wichtigeren" Typen reserviert.
    Wichtig insofern als sie von der CLR stellenweise eine spezielle Behandlung erhalten,
    was der Tabelle integrierter Typen (C#-Referenz) entspricht.

    Alle anderen müssen sich mit TypeCode.Object zufrieden geben.
    Und eine Guid erfährt die so nicht (und wäre als  eine Ansammlung von 11 Feldern kaum geeignet - siehe Konstruktor)

    Für einige wenige kannst Du schreiben:

          Type valueType = value.GetType();
          TypeCode typeCode = Type.GetTypeCode(valueType);
    
          // TimeSpan dazu erfunden ;-)
          if (typeCode == TypeCode.String 
            || typeCode == TypeCode.DateTime
            || (typeCode == TypeCode.Object 
              && (valueType == typeof(Guid) 
                || valueType == typeof(TimeSpan))))
            return true;
    
    
    oder (bei einigen mehr) die Sprungtabelle von C# switch nutzen:

          switch (typeCode)
          {
            case TypeCode.String:
            case TypeCode.DateTime:
            // sinniger wenn noch mehrere kämen
              return true;
            case TypeCode.Object:
              if (valueType == typeof(Guid) || valueType == typeof(TimeSpan)) 
                return true;
              return false;
          }
    
    was IMO auch freundlicher zu lesen ist.

    Sind es einige Typen mehr, so kannst Du ein statisches HashSet<Type> anlegen,
    was dann ein O(1) Zugriff über den Hashcode wäre.

    Gruß Elmar


    Samstag, 2. Juli 2011 04:55
    Beantworter
  • Hallo Elmar,

    mal wieder vielen Dank für deine ausführlich Antwort.

    Dennoch verstehe ich den Sinn der TypeCodes nicht. Ich dachte immer, die wären für einen flotten Typenvergleich für alle grundlegenden Datentypen da. Da aber sowohl mit GUID als auch TimeSpan zumindest mal 2 Grundlegende Datentypen fehlen, scheint TypeCode tatsächlich einem anderen Zweck zu dienen!

    Viele Grüße
    Holger M. Rößler


    Kaum macht man es richtig, schon funktioniert es
    Samstag, 2. Juli 2011 05:55
  • Hallo Holger,

    die TypeCode ist mit Sicherheit nicht für "flotte Vergleiche" geschaffen worden.
    Idealerweise kommt so etwas in Code nicht zu oft vor, sondern es wird so weit / bald als möglich typisiert gearbeitet.

    Herkunft ist das Common Type System und was dort als primitiver Datentyp (siehe Tabelle) eingeordnet wird.
    Und praktisch i. a. (vom JIT-Compiler) auf CPU-nahe Befehle abgebildet werden kann
    bzw. wie System.String eine sehr aufmerksame Sonderhandlung durch die Laufzeit genießt.

    Eine Guid wird nicht direkt abgebildet und hat auch ansonsten für .NET  / CLR keine überragende Bedeutung -
    sie stammt aus der COM Ecke, was z. B. NewGuid zeigt, dass dazu CoCreateGuid bemüht.
    (Und das sie sich im Laufe der Jahre in Datenbanken und anderswo breitgemacht hat,
    ist mehr der Einfallslosigkeit in anderen Bereichen zuzuschreiben ;-).

    Gleiches gilt für die Vielzahl von anderen Typen - TimeSpan hatte ich nur für ein weiteres Oder (||) eingeführt.

    Für CLR / Jitter sind das "gleichwichtige" Typen, die alle über einen Kamm geschoren werden.
    Faktisch wird jeder System.Type durch eine interne Klasse RuntimeType implementiert,
    die wiederum eine RunTypeTypeHandle Struktur bemüht.
    Und dort wird am Ende der TypeCode gecacht - Auszug dazu:

    // System.RuntimeType
    [SecuritySafeCritical]
    protected override TypeCode GetTypeCodeImpl()
    {
    	TypeCode typeCode = this.Cache.TypeCode;
    	if (typeCode != TypeCode.Empty)
    	{
    		return typeCode;
    	}
    	switch (RuntimeTypeHandle.GetCorElementType(this))
    	{
    		case CorElementType.I4:
    		{
    			typeCode = TypeCode.Int32;
    			goto IL_12B;
    		}
    		case CorElementType.String:
    		{
    			typeCode = TypeCode.String;
    			goto IL_12B;
    		}
    
            // ... und die anderen primitiven Typen
    
    		case CorElementType.ValueType:
    		{
    			if (this == Convert.ConvertTypes[15])
    			{
    				typeCode = TypeCode.Decimal;
    				goto IL_12B;
    			}
    			if (this == Convert.ConvertTypes[16])
    			{
    				typeCode = TypeCode.DateTime;
    				goto IL_12B;
    			}
    			if (this.IsEnum)
    			{
    				typeCode = Type.GetTypeCode(Enum.GetUnderlyingType(this));
    				goto IL_12B;
    			}
    			typeCode = TypeCode.Object;
    			goto IL_12B;
    		}
    	}
    // ... gekürzt ...
    
    IL_12B:
    	this.Cache.TypeCode = typeCode;
    	return typeCode;
    }
    
    

     

    Und so einen Cache könnte man (siehe vorherige Antwort) auch selbst verwalten.
    Wenn da nicht einige andere Dinge wären, siehe im Blog von Vance Morrision:
    Drilling into .NET Runtime microbenchmarks: 'typeof' optimizations.

    wo auf einige Implementationsdetails eingegangen wird.
    (Und wo eigener Code bei integralen Typen mit dem Jitter nicht mithalten kann - heute mag da sogar mehr drin sein)

    Gruß Elmar

    Samstag, 2. Juli 2011 08:19
    Beantworter