프로그래밍 방식의 기본값 (Type)


514

리플렉션을 사용하여 Type의 속성 을 반복 하고 특정 유형을 기본값으로 설정합니다. 이제 유형을 전환하고 default(Type)명시 적으로 설정할 수는 있지만 한 줄로 수행하고 싶습니다. 기본적으로 프로그래밍 방식으로 동등한가?


작동해야합니다. Nullable <T> a = new Nullable <T> (). GetValueOrDefault ();
dancer42

답변:


694
  • 값 유형의 경우 Activator.CreateInstance를 사용 하면 제대로 작동합니다.
  • 참조 유형을 사용하는 경우 null을 반환
public static object GetDefault(Type type)
{
   if(type.IsValueType)
   {
      return Activator.CreateInstance(type);
   }
   return null;
}

.net 표준과 같은 최신 버전의 .net에서는 다음과 같이 type.IsValueType작성해야합니다.type.GetTypeInfo().IsValueType


22
이것은 상자 값 유형을 반환하므로 default (Type)과 정확히 일치하지 않습니다. 그러나 제네릭없이 얻을 수있는만큼 가깝습니다.
Russell Giddings

8
그래서 무엇? default(T) != (T)(object)default(T) && !(default(T) != default(T))인수가 있는 유형을 찾으면 상자가 있는지 여부는 중요하지 않습니다.
Miguel Angelo

7
술어의 마지막 부분은 연산자 오버로드로 부정 행위를 피하는 것입니다 ... default(T) != default(T)반환을 거짓으로 만들 수 있습니다 . =)
Miguel Angelo

4
이것은 나에게 큰 도움이되었지만이 질문을 검색하는 사람들에게 유용 할 수있는 한 가지를 추가해야한다고 생각 했습니다. 주어진 유형 의 배열 을 원한다면 동등한 방법 이 있으며를 사용하여 얻을 수 있습니다 Array.CreateInstance(type, length).
Darrel Hoffman

4
알 수없는 값 유형의 인스턴스를 만드는 것에 대해 걱정하지 않습니까? 이것은 부수적 인 영향을 미칠 수 있습니다.
ygormutti

103

리플렉션과 함께 default (T)를 반환하는 메서드를 호출하지 않겠습니까? 다음과 함께 모든 유형의 GetDefault를 사용할 수 있습니다.

    public object GetDefault(Type t)
    {
        return this.GetType().GetMethod("GetDefaultGeneric").MakeGenericMethod(t).Invoke(this, null);
    }

    public T GetDefaultGeneric<T>()
    {
        return default(T);
    }

7
너무 간단하기 때문에 훌륭합니다. 이 방법이 최선의 방법은 아니지만이 기술은 많은 유사한 상황에서 유용 할 수 있으므로 명심해야합니다.
구성자

. 당신은 일반적인 방법 대신 (과부하) "의 getDefault"를 호출하면이 수행합니다. this.GetType () getMethod 메소드 ( "의 getDefault", 새로운 유형 [0]) <AS_IS>
스테판 스타 이거

2
이 구현은 허용 된 답변보다 훨씬 느리게 (반사로 인해) 수행됩니다. 여전히 실행 가능하지만 성능을 향상 시키려면 GetMethod () / MakeGenericMethod () 호출에 대한 일부 캐싱을 설정해야합니다.
Doug

1
type 인수가 void 일 수 있습니다. 예를 들어 void 메소드의 MethodBase.ResultType ()은 이름이 "Void"이거나 FullName이 "System.Void"인 Type 객체를 반환합니다. 따라서 가드를 넣습니다. if (t.FullName == "System.Void")은 null을 반환합니다. 솔루션 주셔서 감사합니다.
Valo

8
더 나은 사용을 nameof(GetDefaultGeneric)대신 할 수 있다면,"GetDefaultGeneric"
Mugen는

87

사용할 수 있습니다 PropertyInfo.SetValue(obj, null). 값 유형에서 호출되면 기본값이 제공됩니다. 이 동작은 .NET 4.0.NET 4.5에 설명되어 있습니다.


7
이 특정 질문-유형의 속성을 반복하고 "기본값"으로 설정하면 훌륭하게 작동합니다. 리플렉션을 사용하여 SqlDataReader에서 개체로 변환 할 때 사용합니다.
Arno Peters

57

당신이 .NET 4.0 이상을 사용하는 사용자가 정의한 규칙의 체계화되지 않은 프로그램 버전하려는 경우 코드의 외부 , 당신은을 만들 수 있습니다Expression 컴파일하여 즉시 실행할 수 있습니다.

다음 확장 방법은 걸릴 Type에서 반환 된 값과 얻을 default(T)스루 Default방법 상의 Expression클래스를 :

public static T GetDefaultValue<T>()
{
    // We want an Func<T> which returns the default.
    // Create that expression here.
    Expression<Func<T>> e = Expression.Lambda<Func<T>>(
        // The default value, always get what the *code* tells us.
        Expression.Default(typeof(T))
    );

    // Compile and return the value.
    return e.Compile()();
}

public static object GetDefaultValue(this Type type)
{
    // Validate parameters.
    if (type == null) throw new ArgumentNullException("type");

    // We want an Func<object> which returns the default.
    // Create that expression here.
    Expression<Func<object>> e = Expression.Lambda<Func<object>>(
        // Have to convert to object.
        Expression.Convert(
            // The default value, always get what the *code* tells us.
            Expression.Default(type), typeof(object)
        )
    );

    // Compile and return the value.
    return e.Compile()();
}

또한를 기반으로 위의 값을 캐시해야 Type하지만 많은 Type인스턴스에 대해이 값을 호출 하고 지속적으로 사용하지 않는 경우 캐시에서 사용하는 메모리가 이점을 능가 할 수 있습니다.


4
'return type.IsValueType의 성능은? Activator.CreateInstance (type) : null; ' e.Compile () ()보다 1000 배 빠릅니다.
Cyrus

1
@Cyrus 캐시를하면 다른 방법이 될 것 e.Compile()입니다. 그것이 표현의 요점입니다.
nawfal

2
벤치 마크를 실행하십시오. 분명히 결과는 e.Compile()캐시되어야하지만이 방법은 예를 들어 대략 14 배 빠르다고 가정합니다 long. 벤치 마크 및 결과는 gist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8b 를 참조하십시오 .
Pieter van Ginkel

3
관심이 없다면 왜 캐시가 e.Compile()아닌 캐시 e.Compile()()일까요? 즉, 런타임에 유형의 기본 유형을 변경할 수 있습니까? 그렇지 않은 경우 (필자가 생각하는 것처럼) 컴파일 된 표현식 대신 결과를 캐시에 저장하면 성능이 더 향상됩니다.
JohnLBevan

3
@JohnLBevan-그렇습니다. 결과를 얻는 데 어떤 기술을 사용하든 상관 없습니다. 모두 빠른 상각 성능 (사전 조회)을 갖습니다.
Daniel Earwicker

38

왜 제네릭이 그림에서 벗어났다고 말합니까?

    public static object GetDefault(Type t)
    {
        Func<object> f = GetDefault<object>;
        return f.Method.GetGenericMethodDefinition().MakeGenericMethod(t).Invoke(null, null);
    }

    private static T GetDefault<T>()
    {
        return default(T);
    }

기호 방법을 확인할 수 없습니다. Windows 용 PCL 사용
Cœur

1
런타임에 일반 메소드를 작성하고 한 번에 수천 번 사용하는 것이 얼마나 비쌉니까?
C. Tewalt

1
나는 이것에 대해 생각하고 있었다. 나를위한 최고의 가장 우아한 솔루션. Compact Framework 2.0에서도 작동합니다. 성능이 걱정된다면 항상 일반적인 방법을 캐시 할 수 있습니까?
Bart

이 솔루션은 정확히 맞습니다! 감사!
Lachezar Lalov

25

이것은 Flem의 솔루션에 최적화되어 있습니다.

using System.Collections.Concurrent;

namespace System
{
    public static class TypeExtension
    {
        //a thread-safe way to hold default instances created at run-time
        private static ConcurrentDictionary<Type, object> typeDefaults =
           new ConcurrentDictionary<Type, object>();

        public static object GetDefaultValue(this Type type)
        {
            return type.IsValueType
               ? typeDefaults.GetOrAdd(type, Activator.CreateInstance)
               : null;
        }
    }
}

2
짧은 반환 버전 :return type.IsValueType ? typeDefaults.GetOrAdd(type, Activator.CreateInstance) : null;
Mark Whitfeld

3
가변 구조체는 어떻습니까? 데이터가 변경되도록 박스 구조의 필드를 수정하는 것이 가능하고 합법적이라는 것을 알고 있습니까?
IllidanS4는 Monica를

메소드 이름으로 @ IllidanS4는 이것이 기본 ValueType의 값에 대해서만 함축합니다.
aderesh

8

선택한 답변은 좋은 답변이지만 반환 된 객체에주의하십시오.

string test = null;
string test2 = "";
if (test is string)
     Console.WriteLine("This will never be hit.");
if (test2 is string)
     Console.WriteLine("Always hit.");

외삽 ...

string test = GetDefault(typeof(string));
if (test is string)
     Console.WriteLine("This will never be hit.");

14
사실이지만 다른 모든 참조 유형과 마찬가지로 default (string)도 유지합니다.
TDaver

string은 홀수 새입니다. null도 반환 할 수있는 값 형식입니다. 코드가 string.empty를 반환하도록하려면 특별한 경우를 추가하십시오
Dror Helper

15
@Dror-문자열은 값 유형이 아닌 변경할 수없는 참조 유형입니다.
ljs

@kronoz 당신이 맞아요-필요에 따라 string.empty 또는 null을 반환하여 문자열을 처리 할 수 ​​있음을 의미했습니다.
Dror Helper

5

식이 여기에 도움이 될 수 있습니다.

    private static Dictionary<Type, Delegate> lambdasMap = new Dictionary<Type, Delegate>();

    private object GetTypedNull(Type type)
    {
        Delegate func;
        if (!lambdasMap.TryGetValue(type, out func))
        {
            var body = Expression.Default(type);
            var lambda = Expression.Lambda(body);
            func = lambda.Compile();
            lambdasMap[type] = func;
        }
        return func.DynamicInvoke();
    }

이 스 니펫을 테스트하지는 않았지만 참조 유형에 대해 "typed"null을 생성해야한다고 생각합니다.


1
"typed" nulls- 설명. 어떤 물건을 반환합니까? 유형의 객체를 반환 type하지만 그 값이 값이면 객체 null이외의 다른 정보를 가질 수 없습니다 null. null값을 쿼리 할 수 ​​없으며 어떤 유형인지 추정 할 수 있습니다 . null을 반환하지 않고 ..을 반환하면 ..을 모른다면 다음과 같이 작동하지 않습니다 null.
ToolmakerSteve

3

아직 간단하고 우아한 것을 찾을 수는 없지만 한 가지 아이디어가 있습니다. 설정하려는 속성의 유형을 알고 있다면 자신만을 작성할 수 있습니다 default(T). 두 가지 경우가 있습니다- T값 유형이고 T참조 유형입니다. 당신은 이것을 확인하여 이것을 볼 수 있습니다 T.IsValueType. 경우 T참조 유형이, 당신은 단순히으로 설정할 수 있습니다 null. 경우 T값 유형이, 그것은 당신이 "빈"값을 얻기 위해 호출 할 수있는 기본 매개 변수가없는 생성자를해야합니다.


3

나는 이와 같은 일을한다.

//in MessageHeader 
   private void SetValuesDefault()
   {
        MessageHeader header = this;             
        Framework.ObjectPropertyHelper.SetPropertiesToDefault<MessageHeader>(this);
   }

//in ObjectPropertyHelper
   public static void SetPropertiesToDefault<T>(T obj) 
   {
            Type objectType = typeof(T);

            System.Reflection.PropertyInfo [] props = objectType.GetProperties();

            foreach (System.Reflection.PropertyInfo property in props)
            {
                if (property.CanWrite)
                {
                    string propertyName = property.Name;
                    Type propertyType = property.PropertyType;

                    object value = TypeHelper.DefaultForType(propertyType);
                    property.SetValue(obj, value, null);
                }
            }
    }

//in TypeHelper
    public static object DefaultForType(Type targetType)
    {
        return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
    }

2

Dror의 답변과 동일하지만 확장 방법 :

namespace System
{
    public static class TypeExtensions
    {
        public static object Default(this Type type)
        {
            object output = null;

            if (type.IsValueType)
            {
                output = Activator.CreateInstance(type);
            }

            return output;
        }
    }
}

2

@Rob Fonseca-Ensor 솔루션에 대한 약간의 조정 : GetMethod 대신 GetRuntimeMethod를 사용하기 때문에 다음 확장 방법도 .Net Standard에서 작동합니다.

public static class TypeExtensions
{
    public static object GetDefault(this Type t)
    {
        var defaultValue = typeof(TypeExtensions)
            .GetRuntimeMethod(nameof(GetDefaultGeneric), new Type[] { })
            .MakeGenericMethod(t).Invoke(null, null);
        return defaultValue;
    }

    public static T GetDefaultGeneric<T>()
    {
        return default(T);
    }
}

... 품질에 관심이있는 사람들을위한 단위 테스트 :

[Fact]
public void GetDefaultTest()
{
    // Arrange
    var type = typeof(DateTime);

    // Act
    var defaultValue = type.GetDefault();

    // Assert
    defaultValue.Should().Be(default(DateTime));
}

0
 /// <summary>
    /// returns the default value of a specified type
    /// </summary>
    /// <param name="type"></param>
    public static object GetDefault(this Type type)
    {
        return type.IsValueType ? (!type.IsGenericType ? Activator.CreateInstance(type) : type.GenericTypeArguments[0].GetDefault() ) : null;
    }

2
작동하지 않습니다 Nullable<T>유형 : 그것의 해당 반환하지 않습니다 default(Nullable<T>)해야합니다 null. Dror의 승인 된 답변이 더 잘 작동합니다.
Cœur

리플렉션을 사용하여 nullable 여부를 확인할 수 있습니다 ...
dancer42

0

이것은 작동해야합니다 : Nullable<T> a = new Nullable<T>().GetValueOrDefault();

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