Varsayılan programatik eşdeğeri (Tür)


514

Yansıma Type'özellikleri arasında döngü ve varsayılan belirli türleri ayarlamak için kullanıyorum. Şimdi, tip üzerinde bir anahtar yapabilir ve default(Type)açıkça ayarlayabilirim , ancak tek bir satırda yapmayı tercih ederim. Varsayılan programatik eşdeğeri var mı?


Bunun çalışması gerekir: Nullable <T> a = new Nullable <T> () .GetValueOrDefault ();
dancer42

Yanıtlar:


694
public static object GetDefault(Type type)
{
   if(type.IsValueType)
   {
      return Activator.CreateInstance(type);
   }
   return null;
}

.Net'in .net standardı gibi yeni sürümlerinde type.IsValueTypeşu şekilde yazılmalıdır:type.GetTypeInfo().IsValueType


22
Bu, kutulu bir değer türü döndürür ve bu nedenle varsayılanın (Tür) tam eşdeğeri değildir. Ancak, jenerikler olmadan alacağınız kadar yakın.
Russell Giddings

8
Ne olmuş yani? Eğer default(T) != (T)(object)default(T) && !(default(T) != default(T))o zaman argümanınız olan bir tür bulursanız , aksi halde kutulu olup olmadığı önemli değildir, çünkü eşdeğerdirler.
Miguel Angelo

7
Yüklemenin son parçası, operatörün aşırı yüklenmesi ile hile yapmaktan kaçınmaktır ... bir kişi default(T) != default(T)dönüşü yanlış yapabilir ve bu hile yapıyor! =)
Miguel Angelo

4
Bu bana çok yardımcı oldu, ancak bu soruyu arayan bazı insanlar için yararlı olabilecek bir şey eklemem gerektiğini düşündüm - verilen türde bir dizi istiyorsanız ve bunu kullanarak da eşdeğer bir yöntem var Array.CreateInstance(type, length).
Darrel Hoffman

4
Bilinmeyen bir değer türünün örneğini oluşturma konusunda endişelenmiyor musunuz? Bunun teminat etkileri olabilir.
ygormutti

103

Neden yansımayla (T) varsayılan döndüren yöntemi çağırmıyorsunuz? Herhangi bir türde GetDefault'u aşağıdakilerle kullanabilirsiniz:

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

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

7
Bu harika çünkü çok basit. Buradaki en iyi çözüm olmasa da, akılda tutulması gereken önemli bir çözümdür, çünkü bu teknik birçok benzer durumda yararlı olabilir.
yapılandırıcı

Bunun yerine "aşırı yükleme" genel yöntemini çağırırsanız (aşırı yükleme), bunu yapın: this.GetType (). GetMethod ("GetDefault", yeni Tip [0]). <AS_IS>
Stefan Steiger

2
Unutmayın, bu uygulama (yansıma nedeniyle) kabul edilen cevaptan çok daha yavaştır. Yine de uygulanabilir, ancak performansı artırmak için GetMethod () / MakeGenericMethod () çağrıları için bazı önbellekleme ayarlamanız gerekir.
Doug

1
Tür argümanının geçersiz olması mümkündür. Bir void yönteminin MethodBase.ResultType () yöntemi, "Void" Adına veya FullName "System.Void" değerine sahip bir Type nesnesi döndürür. Bu yüzden bir bekçi koydum: if (t.FullName == "System.Void") null; Çözüm için teşekkürler.
Valo

8
nameof(GetDefaultGeneric)Mümkünse daha iyi kullanın"GetDefaultGeneric"
Mugen

87

Kullanabilirsiniz PropertyInfo.SetValue(obj, null). Bir değer türünde çağrılırsa, size varsayılan değer verilir. Bu davranış belgelenmiştir .NET 4.0 ve .NET 4.5 .


7
Bu belirli soru için - bir türün özellikleri arasında döngü ve bunları "varsayılan" olarak ayarlama - bu mükemmel çalışıyor. SqlDataReader yansıma kullanarak bir nesneye dönüştürürken kullanıyorum.
Arno Peters

57

.NET 4.0 veya üstünü kullanıyorsanız ve kod dışında tanımlanan kuralların kodlaması olmayan programlı bir sürüm istiyorsanız, bir Expressionderleme oluşturabilir ve anında çalıştırabilirsiniz.

Aşağıdaki uzantı yöntemi , sınıftaki yöntem aracılığıyla Typedöndürülen değeri alır ve alır :default(T)DefaultExpression

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

Yukarıdaki değeri de temel alarak önbelleğe almalısınız Type, ancak bunu çok sayıda Typeörnek için çağırıyorsanız ve sürekli kullanmıyorsanız, önbellek tarafından tüketilen bellek avantajlardan daha ağır basabilir.


4
'Return type.IsValueType için performans? Aktivatör.CreateInstance (tip): null; ' e.Compile () () 'den 1000x daha hızlıdır;
Cyrus

1
@Cyrus Önbelleğe alırsanız bunun tam tersi olacağından oldukça eminim e.Compile(). İfadelerin bütün mesele bu.
nawfal

2
Bir kıyaslama koştu. Açıkçası, sonucun e.Compile()önbelleğe alınması gerekir, ancak bu yöntemin, örneğin kabaca 14 kat daha hızlı olduğu varsayılarak long. Kıyaslama ve sonuçlar için gist.github.com/pvginkel/fed5c8512b9dfefc2870c6853bbfbf8b adresine bakın .
Pieter van Ginkel

3
İlgi alanı dışında, neden önbellek e.Compile()yerine e.Compile()()? ie Bir türün varsayılan türü çalışma zamanında değişebilir mi? Değilse (durumun böyle olduğuna inandığım gibi), yalnızca performansı daha da iyileştirmesi gereken derlenmiş ifade yerine önbelleği depolayabilirsiniz.
JohnLBevan

3
@JohnLBevan - evet, ve sonuç elde etmek için hangi tekniği kullandığınız önemli değildir - hepsi son derece hızlı amortismana tabi tutulmuş performansa (sözlük araması) sahip olacaktır.
Daniel Earwicker

38

Neden jenerik ilaçların resim dışında olduğunu söylüyorsun?

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

Sembol Yöntemi çözülemiyor. Windows için PCL kullanma.
Coeur

1
jenerik yöntemi çalışma zamanında oluşturmak ve daha sonra üst üste birkaç bin kez kullanmak ne kadar pahalı?
C. Tewalt

1
Ben böyle bir şey düşünüyordum. Benim için en iyi ve en zarif çözüm. Compact Framework 2.0 üzerinde bile çalışır. Performans konusunda endişeleriniz varsa, genel yöntemi her zaman önbelleğe alabilirsiniz, değil mi?
Bart

Bu çözüm tam olarak uygun! Teşekkürler!
Lachezar Lalov

25

Bu, Flem'in çözümü için optimize edilmiştir:

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;
Dönüşün

3
Değişken yapılar ne olacak? Kutulu bir yapının alanlarını değiştirmenin mümkün olduğunu (ve yasal olduğunu) biliyor musunuz, böylece veri değişiyor mu?
IllidanS4 Monica'yı

@ IllidanS4, yöntemin adı, bunun yalnızca varsayılan ValueType değerleri için olduğunu gösterir.
aderesh

8

Seçilen cevap iyi bir cevaptır, ancak döndürülen nesneye dikkat edin.

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

Götürdüğümüzde ...

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

14
true, ancak diğer tüm referans türleri gibi varsayılan (dize) için de geçerlidir ...
TDaver

string tuhaf bir kuştur - null değeri de döndürebilen bir değer türüdür. Kodun string.empty döndürmesini istiyorsanız, bunun için özel bir durum ekleyin
Dror Helper

15
@Dror - dize, bir değer türü değil, değiştirilemez bir başvuru türüdür.
ljs

@kronoz Haklısın - Dize, ihtiyaca göre string.empty veya null döndürerek işlenebilir.
Dror Helper

5

İfadeler burada yardımcı olabilir:

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

Ben bu pasajı test etmedi, ama ben referans türleri için "yazılan" nulls üretmek gerektiğini düşünüyorum ..


1
"typed" nulls- açıklamak. Hangi nesneyi iade ediyorsunuz? Bir tür nesnesini döndürürseniz type, ancak değeri şu ise null, bunun dışında başka hiçbir bilgiye sahip olamaz null. Bir nulldeğeri sorgulayamaz ve sözde türünü bulamazsınız. Boş döndürmezseniz, ama geri dönmezseniz .. Ne olduğunu bilmiyorum .., o zaman böyle davranmaz null.
ToolmakerSteve

3

Henüz basit ve zarif bir şey bulamıyorum, ama bir fikrim var: Ayarlamak istediğiniz mülkün türünü biliyorsanız, kendiniz yazabilirsiniz default(T). İki durum vardır - Tbir değer türüdür ve Tbir referans türüdür. Bunu kontrol ederek görebilirsiniz T.IsValueType. Eğer Tbir referans türüdür, o zaman sadece şekilde ayarlayabilirsiniz null. Eğer Tbir değer türüdür, o zaman bir "boş" değeri elde etmek çağırabilir o varsayılan bir parametresiz yapıcı olacaktır.


3

Ben de aynı işi böyle yapıyorum.

//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'un cevabına eşdeğer, ancak bir uzatma yöntemi olarak:

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'un çözümü için küçük ayarlamalar : GetMethod yerine GetRuntimeMethod kullandığımdan dolayı aşağıdaki uzantı yöntemi .Net Standard üzerinde de çalışır.

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

... ve kaliteyi önemseyenler için uygun birim testi:

[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
Türler için çalışmaz Nullable<T>: eşdeğeri default(Nullable<T>)olması gereken değeri döndürmez null. Dror tarafından kabul edilen cevap daha iyi çalışıyor.
Coeur

yansıma kullanarak null olup olmadığını kontrol edebilirsiniz ...
dancer42

0

Bu çalışmalı: Nullable<T> a = new Nullable<T>().GetValueOrDefault();

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.