it-swarm.com.de

Nullable Type als generischer Parameter möglich?

Ich möchte so etwas machen:

myYear = record.GetValueOrNull<int?>("myYear"),

Beachten Sie den Typ nullable als generischen Parameter.

Da die GetValueOrNull -Funktion null zurückgeben konnte, war mein erster Versuch dieser:

public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
  where T : class
{
    object columnValue = reader[columnName];

    if (!(columnValue is DBNull))
    {
        return (T)columnValue;
    }
    return null;
}

Aber der Fehler, den ich jetzt erhalte, ist:

Der Typ 'int?' muss ein Referenztyp sein, um ihn als Parameter 'T' im generischen Typ oder in der generischen Methode zu verwenden

Richtig! Nullable<int> ist ein struct! Also habe ich versucht, die Klassenbeschränkung in eine struct -Einschränkung zu ändern (und als Nebeneffekt kann null nicht mehr zurückgegeben werden):

public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
  where T : struct

Nun die Aufgabe:

myYear = record.GetValueOrNull<int?>("myYear");

Gibt den folgenden Fehler aus:

Der Typ 'int?' muss ein nicht nullwertfähiger Werttyp sein, um ihn als Parameter 'T' im generischen Typ oder in der generischen Methode zu verwenden

Ist die Angabe eines nullfähigen Typs als generischer Parameter überhaupt möglich?

256
Tom Pester

Ändern Sie den Rückgabetyp in Nullable und rufen Sie die Methode mit dem Parameter non nullable auf

static void Main(string[] args)
{
    int? i = GetValueOrNull<int>(null, string.Empty);
}


public static Nullable<T> GetValueOrNull<T>(DbDataRecord reader, string columnName) where T : struct
{
    object columnValue = reader[columnName];

    if (!(columnValue is DBNull))
        return (T)columnValue;

    return null;
}
239
Greg Dean
public static T GetValueOrDefault<T>(this IDataRecord rdr, int index)
{
    object val = rdr[index];

    if (!(val is DBNull))
        return (T)val;

    return default(T);
}

Benutze es einfach so:

decimal? Quantity = rdr.GetValueOrDefault<decimal?>(1);
string Unit = rdr.GetValueOrDefault<string>(2);
102
James Jones

Machen Sie einfach zwei Dinge mit Ihrem ursprünglichen Code - entfernen Sie die where Einschränkung und ändern Sie die letzte return von return null In return default(T). Auf diese Weise können Sie jeden gewünschten Typ zurückgeben.

Übrigens können Sie die Verwendung von is vermeiden, indem Sie Ihre if -Anweisung in if (columnValue != DBNull.Value) ändern.

57
Robert C. Barth

Haftungsausschluss: Diese Antwort funktioniert, ist aber nur für Bildungszwecke gedacht. :) Die Lösung von James Jones ist wahrscheinlich die beste hier und mit Sicherheit die, mit der ich gehen würde.

Das Schlüsselwort dynamic in C # 4.0 macht dies noch einfacher, wenn auch weniger sicher:

public static dynamic GetNullableValue(this IDataRecord record, string columnName)
{
  var val = reader[columnName];

  return (val == DBNull.Value ? null : val);
}

Jetzt brauchen Sie keinen expliziten Typhinweis auf der RHS:

int? value = myDataReader.GetNullableValue("MyColumnName");

Tatsächlich brauchen Sie es gar nicht!

var value = myDataReader.GetNullableValue("MyColumnName");

value ist jetzt ein Int, ein String oder ein Typ, der von der Datenbank zurückgegeben wurde.

Das einzige Problem ist, dass Sie dadurch nicht daran gehindert werden, in der LHS nicht nullfähige Typen zu verwenden.

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'int' because it is a non-nullable value type

Wie bei jedem Code, der dynamic verwendet: Vorbehaltscodierer.

5
Ian Kemp

Musste einfach so etwas Unglaubliches machen. Mein Code:

public T IsNull<T>(this object value, T nullAlterative)
{
    if(value != DBNull.Value)
    {
        Type type = typeof(T);
        if (type.IsGenericType && 
            type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition())
        {
            type = Nullable.GetUnderlyingType(type);
        }

        return (T)(type.IsEnum ? Enum.ToObject(type, Convert.ToInt32(value)) :
            Convert.ChangeType(value, type));
    }
    else 
        return nullAlternative;
}
4
Toby

Ich denke, Sie möchten Referenztypen und Strukturtypen behandeln. Ich verwende es, um XML-Element-Zeichenfolgen in einen typisierteren Typ zu konvertieren. Sie können die nullAlternative mit Reflektion entfernen. Der Formatprovider soll das kulturabhängige '.' oder ',' Trennzeichen in z.B. Dezimalstellen oder Ints und Doubles. Das kann funktionieren:

public T GetValueOrNull<T>(string strElementNameToSearchFor, IFormatProvider provider = null ) 
    {
        IFormatProvider theProvider = provider == null ? Provider : provider;
        XElement Elm = GetUniqueXElement(strElementNameToSearchFor);

        if (Elm == null)
        {
            object o =  Activator.CreateInstance(typeof(T));
            return (T)o; 
        }
        else
        {
            try
            {
                Type type = typeof(T);
                if (type.IsGenericType &&
                type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition())
                {
                    type = Nullable.GetUnderlyingType(type);
                }
                return (T)Convert.ChangeType(Elm.Value, type, theProvider); 
            }
            catch (Exception)
            {
                object o = Activator.CreateInstance(typeof(T));
                return (T)o; 
            }
        }
    }

Du kannst es so benutzen:

iRes = helper.GetValueOrNull<int?>("top_overrun_length");
Assert.AreEqual(100, iRes);



decimal? dRes = helper.GetValueOrNull<decimal?>("top_overrun_bend_degrees");
Assert.AreEqual(new Decimal(10.1), dRes);

String strRes = helper.GetValueOrNull<String>("top_overrun_bend_degrees");
Assert.AreEqual("10.1", strRes);
3
Roland Roos

Dies kann ein toter Thread sein, aber ich neige dazu, Folgendes zu verwenden:

public static T? GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct 
{
    return reader[columnName] as T?;
}
2
Ryan Horch

Ich weiß, das ist alt, aber hier ist eine andere Lösung:

public static bool GetValueOrDefault<T>(this SqlDataReader Reader, string ColumnName, out T Result)
{
    try
    {
        object ColumnValue = Reader[ColumnName];

        Result = (ColumnValue!=null && ColumnValue != DBNull.Value) ? (T)ColumnValue : default(T);

        return ColumnValue!=null && ColumnValue != DBNull.Value;
    }
    catch
    {
        // Possibly an invalid cast?
        return false;
    }
}

Nun ist es Ihnen egal, ob T ein Wert oder ein Referenztyp war. Nur wenn die Funktion true zurückgibt, haben Sie einen angemessenen Wert aus der Datenbank. Verwendungszweck:

...
decimal Quantity;
if (rdr.GetValueOrDefault<decimal>("YourColumnName", out Quantity))
{
    // Do something with Quantity
}

Dieser Ansatz ist int.TryParse("123", out MyInt); sehr ähnlich.

1
nurchi

Ich bin auf dasselbe Problem gestoßen.

... = reader["myYear"] as int?; funktioniert und ist sauber.

Es funktioniert mit jedem Typ ohne Probleme. Wenn das Ergebnis DBNull ist, wird null zurückgegeben, da die Konvertierung fehlschlägt.

1
Hele

Mehrere generische Constraints können nicht auf eine OR= Weise (weniger restriktiv), sondern nur auf eine AND-Weise (restriktiver) kombiniert werden. Dies bedeutet, dass eine Methode nicht beide Szenarien verarbeiten kann. Die generischen Constraints Sie können auch nicht verwendet werden, um eine eindeutige Signatur für die Methode zu erstellen, sodass Sie zwei separate Methodennamen verwenden müssen.

Sie können jedoch die allgemeinen Einschränkungen verwenden, um sicherzustellen, dass die Methoden ordnungsgemäß verwendet werden.

In meinem Fall wollte ich ausdrücklich, dass null und niemals der Standardwert eines möglichen Wertetyps zurückgegeben wird. GetValueOrDefault = schlecht. GetValueOrNull = gut.

Ich habe die Wörter "Null" und "Nullable" verwendet, um zwischen Referenztypen und Werttypen zu unterscheiden. Und hier ist ein Beispiel für ein paar Erweiterungsmethoden, die ich geschrieben habe und die die FirstOrDefault-Methode in der System.Linq.Enumerable-Klasse ergänzen.

    public static TSource FirstOrNull<TSource>(this IEnumerable<TSource> source)
        where TSource: class
    {
        if (source == null) return null;
        var result = source.FirstOrDefault();   // Default for a class is null
        return result;
    }

    public static TSource? FirstOrNullable<TSource>(this IEnumerable<TSource?> source)
        where TSource : struct
    {
        if (source == null) return null;
        var result = source.FirstOrDefault();   // Default for a nullable is null
        return result;
    }
0
Casey