Genel parametre olarak boş değer türü mümkün mü?


288

Böyle bir şey yapmak istiyorum:

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

Null olabilecek türe genel parametre olarak dikkat edin.

Yana GetValueOrNullişlev boş geri dönebilirler benim ilk girişimi şuydu:

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;
}

Ama şimdi aldığım hata:

'İnt?' Türü jenerik tip veya yöntemde 'T' parametresi olarak kullanılabilmesi için bir referans tipi olmalıdır

Sağ! Nullable<int>bir struct! Sınıf kısıtlamasını bir structkısıtlamaya değiştirmeyi denedim (ve bir yan etki olarak artık geri dönemeyeceğim null):

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

Şimdi ödev:

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

Aşağıdaki hatayı verir:

'İnt?' Türü genel tür veya yöntemde 'T' parametresi olarak kullanılabilmesi için boş değerli olmayan bir değer türü olmalıdır

Null olabilecek bir türü genel bir parametre olarak belirtmek mümkün müdür?


3
Lütfen Pls imza yapmak IDataRecordden DbDataRecord..
Nawfal

Yanıtlar:


262

Dönüş türünü Nullable olarak değiştirin ve yöntemi nullable parametresiyle çağırın

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;
}

1
'İs' operatörü yerine "columnValue == DBNull.Value" kullanmanızı öneririm, çünkü biraz daha hızlı =)
dri

40
Kişisel tercih, ancak kısa form T kullanabilirsiniz? Nullable yerine <T>
Dunc

11
Bu değer türleri için iyi, ama sonra C # Nullable <(ref type)> "dize?" Gibi görünmüyor çünkü referans türleriyle (örneğin GetValueOrNull <string>) hiç işe yaramayacağını düşünüyorum. Robert C Barth ve James Jones'un çözümleri aşağıda, ihtiyacınız varsa bana daha iyi geliyor.
bacar

2
@bacar - sağ, dolayısıyla "nerede T: struct", referans türleri istiyorsanız, "where T: class" ile benzer bir yöntem oluşturabilirsiniz
Greg Dean

4
@Greg - tabii, ancak daha sonra ikinci bir yönteme ihtiyacınız var ve adı aşırı yükleyemezsiniz. Dediğim gibi, eğer sen val ve ref türlerini hem işlemek istiyorum, temizleyici çözümleri bu sayfada sunulmaktadır düşünüyorum.
bacar

107
public static T GetValueOrDefault<T>(this IDataRecord rdr, int index)
{
    object val = rdr[index];

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

    return default(T);
}

Sadece şu şekilde kullanın:

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

6
Bu kısaltılabilir: return rdr.IsDBNull (dizin)? varsayılan (T): (T) rdr [dizin];
Foole

11
Bence bu soru açıkça null istiyor , varsayılan değil (T) .
mafu

5
@mafu default (T) referans türleri için null, sayısal tipler için 0 değerini döndürerek çözümü daha esnek hale getirir.
James Jones

2
Ben bu ya çağırmak daha net olduğunu düşünüyorum GetValueOrDefaulto döndüren netleştirmek için default(T)yerine null. Alternatif olarak, Tboş değerli değilse bir istisna atmasını sağlayabilirsiniz .
Sam

Bu yöntemin birçok avantajı vardır ve sizi null dışında bir şey döndürmeyi de düşünmeye zorlar.
Shane

61

Sadece orijinal koduna iki şey - kaldırmak wherekısıtlamayı ve son değiştirmek returngelen return nulletmek return default(T). Bu yolla istediğiniz türü döndürebilirsiniz.

Bu arada, kullanımını önlemek için issizin değiştirerek ifiçin açıklama if (columnValue != DBNull.Value).


4
NULL ve 0 arasında mantıklı bir fark olduğu için bu çözüm işe yaramıyor
Greg Dean

15
Geçtiği tip int mi? Tıpkı istediği gibi NULL döndürecek. İnt türü olarak iletirse, int NULL olamayacağından 0 döndürecektir. Ayrıca denedim ve mükemmel çalışıyor.
Robert C. Barth

2
Bu en doğru ve esnek cevaptır. Ancak, return defaultyeterlidir ( (T)derleyiciye imza dönüş türünden çıkarım yapacaktır).
McGuireV10

5

Feragatname: Bu cevap işe yarar, ancak yalnızca eğitim amaçlıdır. :) James Jones'un çözümü muhtemelen en iyisi ve kesinlikle birlikte gideceğim çözüm.

C # 4.0'ın dynamicanahtar kelimesi, daha az güvenli olduğunda bunu daha da kolaylaştırır:

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

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

Şimdi RHS üzerinde açık bir ipucuna ihtiyacınız yok:

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

Aslında, hiç ihtiyacınız bile yok!

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

value şimdi bir int, bir dize ya da DB'den döndürülen her tür olacaktır.

Tek sorun, bunun LHS'de null olmayan türler kullanmanızı engellememesidir, bu durumda aşağıdaki gibi kötü bir çalışma zamanı istisnası elde edersiniz:

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

Kullanılan tüm kodlarda olduğu gibi dynamic: uyarıcı kodlayıcı.


4

Buna benzer inanılmaz bir şey yapmak zorunda kaldım. Kodum:

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;
}

3

Bence Referans tiplerini ve yapı tiplerini ele almak istiyorsunuz. Ben daha yazılan bir türe XML Element dizeleri dönüştürmek için kullanın. NullAlternative öğesini yansıma ile kaldırabilirsiniz. Biçim sağlayıcı kültüre bağımlı '.' veya ',' ayırıcı, örneğin ondalık sayılar veya ints ve çiftler. Bu işe yarayabilir:

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; 
            }
        }
    }

Bu şekilde kullanabilirsiniz:

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);

2

Bu ölü bir iplik olabilir, ancak aşağıdakileri kullanma eğilimindeyim:

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

1
"'T' türü, 'Nullable <T>' genel türünde veya yönteminde 'T' parametresi olarak kullanılabilmesi için null değeri olmayan bir değer türü olmalıdır"
Ian Warburton

1

Aynı problemle kendim de karşılaştım.

... = reader["myYear"] as int?; çalışır ve temiz.

Sorunsuz herhangi bir tür ile çalışır. Sonuç DBNull ise, dönüştürme başarısız olduğunda null değerini döndürür.


Aslında, muhtemelen int v=reader["myYear"]??-1;yerine ya da başka bir varsayılan yapabilirsiniz -1. Ancak, bu değer ise sorunlara yol DBNull
açabilir

1

Bunun eski olduğunu biliyorum, ama işte başka bir çözüm:

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;
    }
}

Şimdi, Tdeğer veya referans türü olup olmadığı umrunda değil . Yalnızca işlev true değerini döndürürse, veritabanından makul bir değere sahip olursunuz. Kullanımı:

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

Bu yaklaşım, int.TryParse("123", out MyInt);


Adlandırma kurallarınız üzerinde çalışsaydınız iyi olur. Tutarlılıktan yoksundurlar. Bir yerde sermayesi olmayan bir değişken vardır, o zaman bir tane vardır. Yöntemlere parametrelerle aynı.
Marino Šimić

1
Bitti ve bitti! Umut kodu şimdi daha iyi görünüyor. Bob's
auntie

0

Birden fazla genel kısıtlama bir OR tarzında (daha az kısıtlayıcı), yalnızca VE tarzında (daha kısıtlayıcı) birleştirilemez. Yani bir yöntem her iki senaryoyu da işleyemez. Genel kısıtlamalar yöntem için benzersiz bir imza oluşturmak için kullanılamaz, bu nedenle 2 ayrı yöntem adı kullanmanız gerekir.

Ancak, yöntemlerin doğru kullanıldığından emin olmak için genel kısıtlamaları kullanabilirsiniz.

Benim durumumda, özellikle null döndürülmesini istedim ve olası değer türlerinin varsayılan değeri asla. GetValueOrDefault = kötü. GetValueOrNull = iyi.

Referans türleri ile değer türleri arasında ayrım yapmak için "Boş" ve "Boş" sözcüklerini kullandım. Ve burada System.Linq.Enumerable sınıfında FirstOrDefault yöntemini iltifat eden bir çift uzantı yönteminin bir örneği.

    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

Daha kısa yol:

public static T ValueOrDefault<T>(this DataRow reader, string columnName) => 
        reader.IsNull(columnName) ? default : (T) reader[columnName];

dönmek 0için int, ve nulliçinint?

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.