Type değişkeni kullanarak bir değişkeni döküm


281

C # 'da tip nesnesinin bir değişkenini T tipinde bir değişkene atabilir miyim; burada T, Type değişkeninde tanımlanır mı?


12
Kesinlikle konuyla ilgili değil, ancak "döküm" ün ne anlama geldiği konusunda bulanık görünüyorsunuz, döküm operatörünün amacının ve anlamının tam olarak ne olduğunu anlamak iyi bir fikir olabilir. İşte iyi bir başlangıç: blogs.msdn.com/ericlippert/archive/2009/03/19/…
Eric Lippert

2
Bir şey bulduğumu sanıyordum. Bir Typedeğişkeniniz varsa, bu türde bir örnek oluşturmak için yansıma kullanabilirsiniz. Ardından, istediğiniz türü, o türdeki bir parametreden çıkararak döndürmek için genel bir yöntem kullanabilirsiniz. Ne yazık ki, bir türün örneğini oluşturan herhangi bir yansıma yönteminin dönüş türü olacaktır object, bu nedenle genel CastByExampleyönteminiz de kullanır object. Yani bunu yapmanın gerçekten bir yolu yok ve olsa bile, yeni dökülen nesne ile ne yapardınız? Yöntemlerini veya başka bir şeyi kullanamazsınız çünkü türünü bilmiyorsunuz.
Kyle Delaney

@KyleDelaney Teşekkür ederim, tamamen katılıyorum! Benim cevap açıklamaya çalıştı gibi, gerçekten dökme için yararlı olmayan bir şeye a farklı bir şey aslında kullandığınız Tipi tanımlayan bir noktada olmadan. Tiplerin tamamı derleyici zaman tipi kontrolüdür. Sadece nesne üzerinde arama yapmanız gerekiyorsa, objectveya tuşunu kullanabilirsiniz dynamic. Harici modülleri dinamik olarak yüklemek istiyorsanız, sınıfların ortak bir arabirimi paylaşmasını ve nesneyi buna atmasını sağlayabilirsiniz. Üçüncü taraf kodunu kontrol etmiyorsanız, küçük sarmalayıcılar oluşturun ve arayüzü buna uygulayın.
Zyphrax

Yanıtlar:


203

İşte bir cast ve convert örneği:

using System;

public T CastObject<T>(object input) {   
    return (T) input;   
}

public T ConvertObject<T>(object input) {
    return (T) Convert.ChangeType(input, typeof(T));
}

Düzenle:

Yorumlardaki bazı insanlar bu cevabın soruya cevap vermediğini söylüyor. Ancak hat (T) Convert.ChangeType(input, typeof(T))çözüm sağlar. Convert.ChangeTypeYöntem, ikinci argüman olarak verilen Tip herhangi Nesne çevirmeye çalışır.

Örneğin:

Type intType = typeof(Int32);
object value1 = 1000.1;

// Variable value2 is now an int with a value of 1000, the compiler 
// knows the exact type, it is safe to use and you will have autocomplete
int value2 = Convert.ChangeType(value1, intType);

// Variable value3 is now an int with a value of 1000, the compiler
// doesn't know the exact type so it will allow you to call any
// property or method on it, but will crash if it doesn't exist
dynamic value3 = Convert.ChangeType(value1, intType);

Cevabı jeneriklerle yazdım, çünkü gerçek bir tür kullanmadan döküm a somethingyapmak istediğinizde kod kokusunun çok muhtemel bir işareti olduğunu düşünüyorum a something else. % 99.9'unun gerekli olmaması gereken uygun arayüzlerle. Belki de mantıklı olabileceği düşüncesine gelince birkaç son durum vardır, ancak bu davalardan kaçınmanızı tavsiye ederim.

Düzenleme 2:

Birkaç ekstra ipucu:

  • Kodunuzu olabildiğince tipte güvenli tutmaya çalışın. Derleyici türü bilmiyorsa, kodunuzun doğru olup olmadığını kontrol edemez ve otomatik tamamlama gibi şeyler çalışmaz. Basitçe söylemek gerekirse : derleme zamanında tür (ler) i tahmin edemezseniz, derleyici nasıl yapabilir ?
  • Birlikte çalıştığınız sınıflar ortak bir arabirim uyguluyorsa , değeri o arabirime atabilirsiniz. Aksi takdirde kendi arayüzünüzü oluşturmayı düşünün ve sınıfların bu arayüzü uygulamasını sağlayın.
  • Dinamik olarak içe aktardığınız harici kitaplıklarla çalışıyorsanız, ortak bir arabirim olup olmadığını da denetleyin. Aksi takdirde, arabirimi uygulayan küçük sarıcı sınıfları oluşturmayı düşünün.
  • Nesne üzerinde arama yapmak istiyorsanız, ancak türle ilgilenmiyorsanız, değeri bir objectveya dynamicdeğişkeninde saklayın .
  • Jenerikler , dahil olan kesin tipleri bilmeden birçok farklı tip için geçerli yeniden kullanılabilir kod oluşturmanın harika bir yolu olabilir.
  • Sıkışırsanız, farklı bir yaklaşım veya kod refactoru düşünün. Kodunuzun gerçekten bu dinamik olması gerekiyor mu? Var olan herhangi bir türü hesaba katmak zorunda mı?

145
Bu OP'ye nasıl yardımcı olduğunu bilmiyorum. Bir tip değişkeni var, öyle değil T.
nawfal

12
@nawfal, temelde çizgi Convert.ChangeType(input, typeof(T));çözüm verir. typeof(T)Mevcut bir tür değişkeniyle kolayca değiştirebilirsiniz . Daha iyi bir çözüm (mümkünse) dinamik türü bir arada önlemek olacaktır.
Zyphrax

59
@Zyphrax, hayır hala Tmevcut olmayan bir oyuncu kadrosu gerektiriyor .
nawfal

4
Ortaya çıkan nesnenin gerçekten tip olduğunu biliyorum Tama yine de sadece objectbir referans olarak alıyorsunuz . hmm, OP'nin sadece Typedeğişken ve başka hiçbir bilgiye sahip olmadığı öncülünde soruyu ilginç buldum . Yöntem imzası gibi Convert(object source, Type destination):) Yine de ben ur noktası almak
nawfal

10
Bu, bu sorunun çözümü nasıl? Aynı problemim var ve genel bir <T> yok. Sadece bir tür değişkenim var.
Nuri Tasdemir

114

Diğer cevaplar "dinamik" tipten bahsetmez. Bir cevap daha eklemek için, dönüştürülen nesneyi statik türle yayınlamak zorunda kalmadan sonuç nesnenizi saklamak için "dinamik" türü kullanabilirsiniz.

dynamic changedObj = Convert.ChangeType(obj, typeVar);
changedObj.Method();

"Dinamik" kullanımıyla derleyicinin, dikkatli değilseniz olası çalışma zamanı hataları oluşturabilecek statik tür denetimini atladığını unutmayın.


19
Bu doğru cevap. Dinamik anahtar kelime olmadan typeof (changingObj) "object" olur. Dinamik anahtar kelime ile kusursuz çalışır ve typeof (changingObject), typeVar ile aynı türü doğru şekilde yansıtır. Ayrıca, türü bilmiyorsanız yapamayacağınız (T) 'ye gerek yoktur.
rushinge

5
Bu çözümü kullanırken "Nesne IConvertible uygulamak gerekir" özel durum var. Herhangi bir yardım?
Nuri Tasdemir

@NuriTasdemir Söylemesi zor, ama yaptığınız dönüşümün IConvertible olmadan mümkün olmadığına inanıyorum. Dönüşümünüze dahil olan türler nelerdir?
maulik13

Bu çalışırken dinamik kullanımda bir performans cezası vardır. Diğer çalışma zamanlarıyla (dinamiklerin ne için tasarlandığı) çalışmadığınız sürece bunları kullanmamanızı tavsiye ederim.
Bolo

19

İşte benim nesne bir System.Typedinamik değil, genel bir tür değişkenine değil, bir nesne döküm yöntemi :

Girdi kutusundan, türünü istenen tür dönüşüm gerçekleştirir System.Linq.Expressionstürünü kullanarak türünü kullanarak çalışma zamanında lambda ifade oluşturmak Func<object, object>sonra sonuç kutulu verir. Sadece dökülen tüm tipler için değil, aynı zamanda dökülen tipler için de (kutu açma adımı nedeniyle) yeni bir tane gereklidir. Bu ifadelerin oluşturulması, kaputun altında yapılan yansıma, derleme ve dinamik yöntem oluşturma nedeniyle oldukça zaman alıcıdır. Neyse ki oluşturulduktan sonra, ifadeler tekrar tekrar ve yüksek ek yük olmadan çağrılabilir, bu yüzden her birini önbelleğe alırım.

private static Func<object, object> MakeCastDelegate(Type from, Type to)
{
    var p = Expression.Parameter(typeof(object)); //do not inline
    return Expression.Lambda<Func<object, object>>(
        Expression.Convert(Expression.ConvertChecked(Expression.Convert(p, from), to), typeof(object)),
        p).Compile();
}

private static readonly Dictionary<Tuple<Type, Type>, Func<object, object>> CastCache
= new Dictionary<Tuple<Type, Type>, Func<object, object>>();

public static Func<object, object> GetCastDelegate(Type from, Type to)
{
    lock (CastCache)
    {
        var key = new Tuple<Type, Type>(from, to);
        Func<object, object> cast_delegate;
        if (!CastCache.TryGetValue(key, out cast_delegate))
        {
            cast_delegate = MakeCastDelegate(from, to);
            CastCache.Add(key, cast_delegate);
        }
        return cast_delegate;
    }
}

public static object Cast(Type t, object o)
{
    return GetCastDelegate(o.GetType(), t).Invoke(o);
}

Bunun sihir olmadığını unutmayın. Kodda, dynamicanahtar kelimeyle olduğu gibi, yalnızca nesnenin temel verileri dönüştürülür. Derleme zamanında, nesnemizin ne tür olabileceğini titizlikle anlamaya devam ediyoruz ve bu çözümü pratik yapmıyoruz. Bunu rastgele türler tarafından tanımlanan dönüşüm operatörlerini çağırmak için bir hack olarak yazdım, ancak belki de birileri daha iyi bir kullanım durumu bulabilir.


2
Gerekenusing System.Linq.Expressions;
Aaron D

4
Benim için bu Zyphrax'ın cevabı ile aynı problemden muzdarip. Hala "nesne" türünde olduğu için döndürülen nesne üzerinde yöntemleri çağıramıyorum. Yöntemini (aşağıda "a") ya da yönteminizi (aşağıda "b") kullansam da (t) Type t = typeof(MyGeneric<>).MakeGenericType(obj.OutputType); var a = (t)Convert.ChangeType(obj, t); var b = (t)Caster.Cast(t, obj);
cast'ta

@muusbolla Zyphrax'ın orijinal cevabı jenerikleri ve tip değişkenlerini kullanır, değil Type. Sahip olduğunuz tek şey Type nesnesi ise normal döküm sözdizimini kullanarak yayın yapamazsınız. Nesneyi çalışma zamanında değil, derleme zamanında bir T türü olarak kullanmak istiyorsanız, bir tür değişkeni veya yalnızca gerçek tür adını kullanarak yayınlamanız gerekir. İlkini Zaphrax'ın cevabını kullanarak yapabilirsiniz.
Ashley

8

Boks ve kutuyu basitleştirmek için bir kenara bıraktığınızda, miras hiyerarşisi boyunca dökümle ilgili belirli bir çalışma zamanı eylemi yoktur. Çoğunlukla derleme zamanı. Esasen, bir kadro, derleyiciye değişkenin değerini başka bir tür olarak ele almasını söyler.

Oyunculardan sonra ne yapabilirsiniz? Türü bilmiyorsunuz, bu yüzden üzerinde herhangi bir yöntem çağıramazsınız. Yapabileceğin özel bir şey olmazdı. Özellikle, yalnızca derleme zamanında olası türleri biliyorsanız, manuel olarak yayınlar ve her bir vakayı ififadelerle ayrı ayrı ele alırsanız yararlı olabilir :

if (type == typeof(int)) {
    int x = (int)obj;
    DoSomethingWithInt(x);
} else if (type == typeof(string)) {
    string s = (string)obj;
    DoSomethingWithString(s);
} // ...

1
Sorumla ilgili olarak daha net açıklar mısınız?
theringostarrs

Açıklamaya çalıştığım şey, bundan sonra ne yapabilirsin? C # derleyicisi, nesne ile yararlı bir şey yapabilmek için statik yazmayı gerektirdiğinden fazla bir şey yapamazsınız
Mehrdad Afshari

Haklısın. Yönteme 'nesne' olarak gönderilen iki değişkenin beklenen tiplerini biliyorum. Değişkenlerde saklanan beklenen türlere dökmek ve koleksiyona eklemek istiyorum. Tip üzerinde dallamak ve normal bir döküm ve yakalama hataları denemek çok daha kolay.
theringostarrs

4
Cevabınız iyi, ama sadece nit seçici olmak için, kadroların asla değişkenleri etkilemediğini not ediyorum . Bir değişkeni başka bir değişkene dönüştürmek asla yasal değildir ; değişken türleri C # 'da değişmezdir. Değişkende depolanan değeri yalnızca başka bir türe atayabilirsiniz.
Eric Lippert

C # 4.0'ın dinamik yazma özelliği bu yanıtı değiştiriyor mu?
Daniel T.

6

Bunu nasıl yapabildin? Dökümden sonra nesneyi depolayabileceğiniz T tipi bir değişkene veya alana ihtiyacınız vardır, ancak T'yi yalnızca çalışma zamanında biliyorsanız, böyle bir değişkene veya alana nasıl sahip olabilirsiniz? Yani, hayır, bu mümkün değil.

Type type = GetSomeType();
Object @object = GetSomeObject();

??? xyz = @object.CastTo(type); // How would you declare the variable?

xyz.??? // What methods, properties, or fields are valid here?

3
T türünde dönüş değeri olan bir yöntem tanımlayan genel bir sınıf kullanıyorsanız, bunu yapmanız gerekebilir. Örneğin, bir dizeyi T örneğine ayrıştırma ve döndürme.
Oliver Friedrich

7
Neyse ki bu doğru cevap değil. Maulik13'ün cevabına bakınız.
rushinge

3
Cennetin adının neresinde bir CastToyöntem buluyorsun Object?
ProfK

3

Enum tipine döküm söz konusu olduğunda:

private static Enum GetEnum(Type type, int value)
    {
        if (type.IsEnum)
            if (Enum.IsDefined(type, value))
            {
                return (Enum)Enum.ToObject(type, value);
            }

        return null;
    }

Ve şöyle diyeceksiniz:

var enumValue = GetEnum(typeof(YourEnum), foo);

İnt değerine göre birkaç enum türünün Description özniteliği değerini almak durumunda bu benim için önemliydi:

public enum YourEnum
{
    [Description("Desc1")]
    Val1,
    [Description("Desc2")]
    Val2,
    Val3,
}

public static string GetDescriptionFromEnum(Enum value, bool inherit)
    {
        Type type = value.GetType();

        System.Reflection.MemberInfo[] memInfo = type.GetMember(value.ToString());

        if (memInfo.Length > 0)
        {
            object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), inherit);
            if (attrs.Length > 0)
                return ((DescriptionAttribute)attrs[0]).Description;
        }

        return value.ToString();
    }

ve sonra:

string description = GetDescriptionFromEnum(GetEnum(typeof(YourEnum), foo));
string description2 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum2), foo2));
string description3 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum3), foo3));

Alternatif olarak (daha iyi yaklaşım), bu tür dökümler şöyle görünebilir:

 private static T GetEnum<T>(int v) where T : struct, IConvertible
    {
        if (typeof(T).IsEnum)
            if (Enum.IsDefined(typeof(T), v))
            {
                return (T)Enum.ToObject(typeof(T), v);
            }

        throw new ArgumentException(string.Format("{0} is not a valid value of {1}", v, typeof(T).Name));
    }

1

Zyphrax'ın cevabını (arayüzün uygulanması hariç) kullanırken "Nesne IConvertible uygulamalıdır" istisnası etrafında bir şey bulamadıktan sonra .. Biraz alışılmadık bir şey denedim ve durumum için çalıştı.

Newtonsoft.Json nuget paketini kullanarak ...

var castedObject = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(myObject), myType);

1

Zarar, sorun şu ki T yok.

yalnızca bir Type değişkeniniz var.

MS'e ipucu, eğer böyle bir şey yapabilirsen

TryCast<typeof(MyClass)>

eğer tüm sorunları çözecekti.


0

Bir yorum bırakmak için neden 50'ye kadar itibara ihtiyacınız olduğunu asla anlamayacağım ama sadece @Curt cevabının tam olarak aradığım şey olduğunu ve umarım bir başkası olduğunu söylemek zorundaydım.

Örneğimde, json düzeltme eki belgesinin değerlerini güncellemek için kullandığım ActionFilterAttribute var. T modeli için yama belgesi ne oldu ben seri hale getirmek ve düz bir JsonPatchDocument onu serileştirmek zorunda kaldı, onu değiştirmek, sonra türü vardı, tekrar serileştirmek ve serisini tekrar türüne.

Type originalType = //someType that gets passed in to my constructor.

var objectAsString = JsonConvert.SerializeObject(myObjectWithAGenericType);
var plainPatchDocument = JsonConvert.DeserializeObject<JsonPatchDocument>(objectAsString);

var plainPatchDocumentAsString= JsonConvert.SerializeObject(plainPatchDocument);
var modifiedObjectWithGenericType = JsonConvert.DeserializeObject(plainPatchDocumentAsString, originalType );

-1
public bool TryCast<T>(ref T t, object o)
{
    if (
        o == null
        || !typeof(T).IsAssignableFrom(o.GetType())
        )
        return false;
    t = (T)o;
    return true;
}

2
Lütfen bu cevabın diğer cevaplardan nasıl farklı olduğunu ve bu çözümün nerede uygun olduğunu belirtir misiniz?
Klaus Gütter

-2

daha temiz:

    public static bool TryCast<T>(ref T t, object o)
    {
        if (!(o is T))
        {
            return false;
        }

        t = (T)o;
        return true;
    }

-2

Hedef türünü bilmeden çalışma zamanında nesneleri yayınlamanız gerekiyorsa, dinamik bir dönüştürücü yapmak için yansımayı kullanabilirsiniz.

Bu basitleştirilmiş bir sürümdür (oluşturulan yöntemi önbelleğe almadan):

    public static class Tool
    {
            public static object CastTo<T>(object value) where T : class
            {
                return value as T;
            }

            private static readonly MethodInfo CastToInfo = typeof (Tool).GetMethod("CastTo");

            public static object DynamicCast(object source, Type targetType)
            {
                return CastToInfo.MakeGenericMethod(new[] { targetType }).Invoke(null, new[] { source });
            }
    }

o zaman arayabilirsiniz:

    var r = Tool.DynamicCast(myinstance, typeof (MyClass));
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.