2010-09-18 15 views
21

3.0 aşağıdaki yazım bir sınıf oluşturmak için Expression kullanabilirsiniz:Anonim Tip oluşturmak için İfade nasıl kullanılır? C# '

var exp = Expression.New(typeof(MyClass)); 
var lambda = LambdaExpression.Lambda(exp); 
object myObj = lambda.Compile().DynamicInvoke(); 

Ama nasıl bir anonim sınıf oluşturmak için Expression kullanırım? anonim bir türü varsayılan bir boş bir yapıcı olmadığından

//anonymousType = typeof(new{ Name="abc", Num=123}); 
Type anonymousType = Expression.NewAnonymousType??? <--How to do ? 
var exp = Expression.New(anonymousType); 
var lambda = LambdaExpression.Lambda(exp); 
object myObj = lambda.Compile().DynamicInvoke(); 
+0

Olası kopyası: http://stackoverflow.com/questions/606104/linq-expression-tree-question –

+1

@ Flash, bu mümkün değil, en azından doğrudan. Anonim, anonim tipler oluşturduğunuzda sizin için bir çok "sihir" yapar - gerçek bir C# sınıfını bir grup özellik ile bildirmek için sözdizimsel şekerdir. Derleyici tüm bunları sizin için yapar. Tüm bunları sizin için otomatik olarak yapan bir ifade ağacı türü yoktur. Başvurulan bağlantıya bakarsanız, bir geçici çözüm sağlar. Bununla birlikte, kalbin çekişi için olmayan Reflection.Emit'i kullanır. –

+1

Kirk: OP, * anonim bir sınıf oluşturmak * ister * sıfırdan birini * oluşturmak * ister. Derleme zamanında, özelliklerin isimlerinin ve türlerinin ne olduğunu bildiği sürece, derleyiciyi onun için bir tür oluşturmasını sağlayabilir ve yapması gereken tek şey bunun nasıl örnek olacağıdır. – Gabe

cevap

17

Sen yakınsın ama anonim türleri varsayılan kurucular yok farkında olmak zorunda. Aşağıdaki kod baskılar { Name = def, Num = 456 }: Bu tip pek çok örneğini oluşturmak için yoksa

Type anonType = new { Name = "abc", Num = 123 }.GetType(); 
var exp = Expression.New(
      anonType.GetConstructor(new[] { typeof(string), typeof(int) }), 
      Expression.Constant("def"), 
      Expression.Constant(456)); 
var lambda = LambdaExpression.Lambda(exp); 
object myObj = lambda.Compile().DynamicInvoke(); 
Console.WriteLine(myObj); 

, Activator.CreateInstance (birçok daha hızlı birkaç örnekleri için, ama daha yavaş) sadece de yapacağız. Bu kod { Name = ghi, Num = 789 } yazdırır:

Type anonType = new { Name = "abc", Num = 123 }.GetType(); 
object myObj = Activator.CreateInstance(anonType, "ghi", 789); 
Console.WriteLine(myObj); 
+2

olup Fakat eğer – Flash

+2

@Flash: Bir LINQ ifadesi kullanıldığında C# kodu 'yeni {Name = "abc", Num = 123}', çalışma anında yeni bir tür oluşturur izlenimi altında ise, o zaman yanılıyorsunuz. Derleyici, derleme zamanında türünü oluşturur ve oluşturulan Expression Tree, anonim olmayan bir tür kullanan birinden ayırt edilemez. – Timwi

+0

Flash: İstediğiniz * dinamik anonim * türleri? Onlarla ne yapmayı düşünüyorsun? Ben buldukça anonim türünü tanımlarken 'buna daha net dan tanımını ayırmak için yapar; – Gabe

6

, sen Expression.New(Type) aşırı kullanamazsınız ... sen Expression.New yöntemine ConstructorInfo ve parametreleri sağlamak zorunda. Bunu yapmak için , bunu yapmak için , ... anonim tür bir "saplama" örneği oluşturmanız gerekir ve Type ve ConstructorInfo almak için kullanın ve sonra parametrelere geçirin. Expression.New yöntemi. Bunun gibi

:

var exp = Expression.New(new { Name = "", Num = 0 }.GetType().GetConstructors()[0], 
         Expression.Constant("abc", typeof(string)), 
         Expression.Constant(123, typeof(int))); 
var lambda = LambdaExpression.Lambda(exp); 
object myObj = lambda.Compile().DynamicInvoke(); 
+1

Bu akıllı bir çözümdür. Ancak genellikle ifade ağaçları kullanarak bir şey yazmanın gerekliliği (API) tam olarak bir * bu bilgiyi derleme zamanında almaz. Eğer yapmış olsaydı, ilk etapta sıradan C# ifadelerini kullanırlardı. –

+0

@Kirk OPs kodu farklı görünmektedir. Ve orada türünü bildiğiniz ama yine de bir ExpressionTree oluşturmak zorunda olduğunuz birçok durum var. DynamicLinq-2-SQL anonim tip ''} {yeni :) –

+0

Sadece nitpicking biri anonim türleri boş Kurucular için, Tip anonType = new {Name = "abc", Num = 123} .GetType() ; <- Bu statik kod, dinamik kod değil. – nawfal

3

Sen acı yavaş DynamicInvoke kullanarak önleyebilirsiniz. Anonim tipinizi genel olarak örneklendirmek için C# cinsinden tür çıkarımını kullanabilirsiniz. Bir şey gibi:

Şimdi
public static Func<object[], T> AnonymousInstantiator<T>(T example) 
{ 
    var ctor = typeof(T).GetConstructors().First(); 
    var paramExpr = Expression.Parameter(typeof(object[])); 
    return Expression.Lambda<Func<object[], T>> 
    (
     Expression.New 
     (
      ctor, 
      ctor.GetParameters().Select 
      (
       (x, i) => Expression.Convert 
       (
        Expression.ArrayIndex(paramExpr, Expression.Constant(i)), 
        x.ParameterType 
       ) 
      ) 
     ), paramExpr).Compile(); 
} 

Arayabileceğin,

var instantiator = AnonymousInstantiator(new { Name = default(string), Num = default(int) }); 

var a1 = instantiator(new object[] { "abc", 123 }); // strongly typed 
var a2 = instantiator(new object[] { "xyz", 789 }); // strongly typed 
// etc. 

Sen uygun bir geçmek zorunda sadece, özelliklerden herhangi birini sayı ile herhangi anonim tür örneğini fonksiyonlarını oluşturmak için AnonymousInstantiator yöntemi kullanabilirsiniz önce örnek. Giriş parametreleri bir nesne dizisi olarak geçirilmelidir. Boks performansında endişe ederseniz, sadece string ve int kabul parametrelerini giriş parametreleri olarak kabul eden özel bir başlatıcı yazmanız gerekir, ancak böyle bir başlatıcının kullanımı biraz daha sınırlı olacaktır.

İlgili konular