일반적인 매개 변수로 널 입력 가능 유형?


288

나는 이런 식으로하고 싶다 :

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

널 입력 가능 유형을 일반 매개 변수로 확인하십시오.

GetValueOrNull함수가 null을 반환 할 수 있기 때문에 첫 번째 시도는 다음과 같습니다.

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

그러나 내가 지금 얻는 오류는 다음과 같습니다.

'int?'유형 제네릭 형식 또는 메서드에서 매개 변수 'T'로 사용하려면 참조 형식이어야합니다.

권리! Nullable<int>입니다 struct! 그래서 클래스 제약 조건을 제약 조건으로 변경하려고 시도했지만 struct부작용으로 null더 이상 반환 할 수 없습니다 .

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

이제 과제 :

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

다음과 같은 오류가 발생합니다.

'int?'유형 일반 유형 또는 메소드에서 매개 변수 'T'로 사용하려면 널 입력 불가능 값 유형이어야합니다.

널 입력 가능 유형을 일반 매개 변수로 지정할 수 있습니까?


3
Pls pls IDataRecord에서 귀하의 서명 을 DbDataRecord..
nawfal

답변:


262

반환 유형을 Nullable로 변경하고 nullable이 아닌 매개 변수를 사용하여 메서드를 호출하십시오.

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
약간 더 빠르기 때문에 'is'연산자 대신 "columnValue == DBNull.Value"를 사용하는 것이 좋습니다.
driAn

40
개인 취향이지만 짧은 형식 T를 사용할 수 있습니까? Nullable <T> 대신
Dunc

11
이것은 값 유형에 대해서는 좋지만 C #이 "string?"과 같은 Nullable <(ref type)>을 좋아하지 않기 때문에 참조 유형 (예 : GetValueOrNull <string>)에서는 전혀 작동하지 않을 것이라고 생각합니다. 아래의 Robert C Barth와 James Jones의 솔루션은 귀하의 필요에 따라 훨씬 나아 보입니다.
bacar

2
@bacar-맞다. 따라서 "where T : struct", 참조 타입을 원한다면 "where T : class"와 비슷한 방법을 만들 수있다
Greg Dean

4
@Greg-확실하지만 두 번째 방법이 필요하며 이름을 오버로드 할 수 없습니다. 내가 말할 때, 경우에 당신이 발과 심판 유형을 모두 처리하려면, 난 청소기 솔루션이 페이지에 표시됩니다 생각합니다.
바카

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

다음과 같이 사용하십시오.

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

6
return rdr.IsDBNull (index)? 디폴트 (T) : (T) rdr [인덱스];
Foole

11
이 질문에 default (T)가 아닌 null 이 명시 적으로 필요하다고 생각합니다 .
mafu

5
@mafu default (T)는 참조 유형의 경우 null을, 숫자 유형의 경우 0을 반환하여 솔루션을보다 유연하게 만듭니다.
James Jones

2
나는 이것이 아니라 GetValueOrDefault반환한다는 것을 명확히하기 위해 이것을 호출하는 것이 더 분명하다고 생각합니다 . 또는 널 입력 가능하지 않은 경우 예외 가 발생하도록 할 수 있습니다 . default(T)nullT
Sam

이 방법에는 많은 장점이 있으며 null 이외의 것을 반환하는 것에 대해 생각해야합니다.
Shane

61

원래 코드에 대해 두 가지 작업을 수행하십시오. where제약 조건을 제거 하고 마지막을 return에서 return null로 변경하십시오 return default(T). 이렇게하면 원하는 유형을 반환 할 수 있습니다.

그런데 명령문을 is로 변경하여 사용을 피할 수 있습니다 .ifif (columnValue != DBNull.Value)


4
NULL과 0 사이에 논리적 차이가 있으므로이 솔루션은 작동하지 않습니다.
Greg Dean

15
그가 전달하는 유형이 int? 인 경우 작동합니다. 원하는대로 NULL을 반환합니다. 그가 int를 타입으로 전달하면 int는 NULL이 될 수 없으므로 0을 반환합니다. 내가 그것을 시도하고 완벽하게 작동한다는 사실 외에도.
Robert C. Barth

2
이것이 가장 정확하고 유연한 답변입니다. 그러나 return default충분합니다 ( (T)컴파일러가 필요하지 않으면 컴파일러가 서명 반환 유형에서 유추합니다).
McGuireV10 2012 년

5

면책 조항 : 이 답변은 효과가 있지만 교육 목적으로 만 사용됩니다. :) James Jones의 솔루션은 아마도 여기에서 가장 좋을 것입니다.

C # 4.0의 dynamic키워드를 사용하면 안전성이 떨어지더라도 훨씬 쉽게 만들 수 있습니다.

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

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

이제 RHS에 대한 명시 적 유형 힌트가 필요하지 않습니다.

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

사실, 당신은 전혀 필요하지 않습니다!

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

value 이제 int 또는 문자열이거나 DB에서 반환 된 모든 유형이됩니다.

유일한 문제는 이것이 LHS에서 널 입력 불가능 유형을 사용하는 것을 방해하지 않는다는 것입니다.

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

사용하는 모든 코드와 마찬가지로 dynamic:주의 코더.


4

이것과 비슷한 것을해야했습니다. 내 코드 :

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

참조 유형과 구조체 유형을 처리하고 싶다고 생각합니다. XML 요소 문자열을 더 유형이 지정된 유형으로 변환하는 데 사용합니다. 리플렉션을 사용하여 nullAlternative를 제거 할 수 있습니다. formatprovider는 문화에 의존하는 '.'을 처리하는 것입니다. 또는 ','구분 기호 (예 : 10 진수 또는 정수 및 2 배) 이것은 작동 할 수 있습니다 :

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

다음과 같이 사용할 수 있습니다.

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

스레드가 끊겼을 수도 있지만 다음을 사용하는 경향이 있습니다.

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

1
"일반 유형 또는 메소드 'Nullable <T>'에서 매개 변수 'T'로 사용하려면 'T'유형은 널 입력 불가능 값 유형이어야합니다."
Ian Warburton

1

방금 같은 문제가 발생했습니다.

... = reader["myYear"] as int?; 작동하고 깨끗합니다.

문제없이 모든 유형에서 작동합니다. 결과가 DBNull 인 경우 변환이 실패하면 null이 반환됩니다.


실제로, int v=reader["myYear"]??-1;대신 또는 다른 기본값을 사용할 수 -1있습니다. 그러나 가치가 다음과 같은 경우 문제가 발생할 수 있습니다 DBNull.
nurchi

1

나는 이것이 오래된 것을 알고 있지만 여기에 다른 해결책이 있습니다.

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

이제 T값 또는 참조 유형인지 상관하지 않습니다 . 함수가 true를 리턴하는 경우에만 데이터베이스에서 합리적인 값을 갖습니다. 용법:

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

이 방법은 int.TryParse("123", out MyInt);


명명 규칙에 따라 작업하면 좋을 것입니다. 일관성이 부족합니다. 한 곳에 자본이없는 변수가 있고 그 뒤에 수도 있습니다. 메소드의 매개 변수와 동일합니다.
마리노 시미치

1
완료했다! 희망 코드가 이제 더 좋아 보입니다. 밥은 네 아줌마 :) 모두 skookum입니다
nurchi

0

여러 일반 제약 조건은 OR 방식 (제한 없음)으로 결합 할 수 없으며 AND 방식 (더 제한적)으로 만 결합 할 수 없습니다. 한 가지 방법으로 두 시나리오를 모두 처리 할 수는 없습니다. 또한 일반 제약 조건을 사용하여 메서드에 고유 한 서명을 만들 수 없으므로 두 개의 별도 메서드 이름을 사용해야합니다.

그러나 일반 제한 조건을 사용하여 메소드가 올바르게 사용되는지 확인할 수 있습니다.

필자의 경우 특히 null을 반환하려고했지만 가능한 값 유형의 기본값은 절대로 사용하지 않았습니다. GetValueOrDefault = 잘못되었습니다. GetValueOrNull = 양호

"Null"과 "Nullable"이라는 단어를 사용하여 참조 유형과 값 유형을 구분했습니다. 그리고 여기 System.Linq.Enumerable 클래스의 FirstOrDefault 메소드를 보완하는 몇 가지 확장 메소드의 예가 있습니다.

    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

더 짧은 방법 :

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

반환 0을 위해 int, 그리고 null에 대한int?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.