2009-10-21 16 views
51

我有一個類象下面這樣:一個構造函數作爲代表 - 它可能在C#中嗎?

class Foo 
{ 
    public Foo(int x) { ... } 
} 

,我需要通過一定的方法,這樣的委託:

delegate Foo FooGenerator(int x); 

是否有可能通過構造函數直接作爲FooGenerator值,無需輸入:

delegate(int x) { return new Foo(x); } 

編輯:對於我個人的使用,這個問題是指.NET 2.0,但3.0 +的提示/響應也是受歡迎的。

+0

有趣的問題。就CLR而言,我認爲構造函數是有效的方法,但我不知道語法。 – Noldorin 2009-10-21 13:06:57

+3

我很感興趣:你爲什麼要這麼做? – 2009-10-21 13:07:34

+0

但我懷疑答案是否定的。 – Noldorin 2009-10-21 13:07:46

回答

27

不,在CLR不允許綁定委託給ConstructorInfo

但是,您可以只創建自己:

static T Make<T>(Action<T> init) where T : new() 
{ 
    var t = new T(); 
    init(t); 
    return t; 
} 

使用

var t = Make<Foo>(x => { x.Bar = "bar"; x.Baz = 1; }); 
+0

你可能會添加一些引用(MSDN鏈接?)你的關於綁定到ConstructorInfo的聲明? – akavel 2009-10-21 14:52:07

+7

構造函數不會產生新的對象。構造函數與分配例程一起工作。 – user7116 2009-10-21 15:42:20

+0

@sixlettervariables:+1 - 看起來像一個合理的解釋。也就是說,我仍然很想看到某些MSDN/C#-specification/...引用。 – akavel 2009-10-21 16:07:14

2

不幸的是,構造函數與方法不完全相同,因此您無法創建指向它們的委託。儘管這是一個有趣的想法,但也許有了更多的信息,我們可以設計出一些類似於句法的解決方法。

+0

您能否詳細說明「代表的構造函數不完全(...)爲方法」的陳述?我特別喜歡MSDN/C#參考/其他文檔的一些參考。 – akavel 2009-10-21 15:44:58

+0

答案沒有反應,唯一提供的信息是'不','構造函數與方法不完全相同'(當然不是)。是的另一個答案,假裝解釋爲什麼不可能說「它不是」。 – jwg 2013-03-28 10:22:29

5

聽起來你可能想要使用類工廠模式。

Factory Method Pattern

+0

這實際上是我用來解決問題的方法,但是爲了編寫問題,我認爲'委託'構造更容易理解。 – akavel 2009-10-21 14:56:37

+0

工廠方法模式很好,但只有在編譯時知道所有可能的類型。 – 2016-01-12 16:00:05

0

我的猜測是,這是不可能的,因爲你會通過還沒有被創建的對象的方法。

7

我想盡量精簡,你會得到(沒有移動到工廠模式)將是某種匿名方法,像這樣:

delegate Foo FooGenerator(int x); 

...  

void DoStuff() 
{ 
    YourDelegateConsumer(x => new Foo(x)); 
} 

這是沒有做嚴格的你要的(因爲你傳遞一個委託給一個匿名方法,返回一個新的實例,而不是直接委託給構造函數),但我不認爲你所要求的是完全可能的。

這是當然的,假設你使用3.5+

+0

+1;我實際上正在編譯2.0,這就是爲什麼我必須使用「委託」,但作爲核心問題是關於其他問題,lambda構造肯定會被記住。 – akavel 2009-10-21 14:31:41

49

我假設你通常會做這樣的事情作爲一個工廠實現,其中實際類型AREN的一部分在編譯時不知道...

首先,請注意,更簡單的方法可能是創建初始化步驟,然後您可以使用泛型:

static T Create<T>({args}) where T : class, ISomeInitInterface, new() { 
    T t = new T(); 
    t.Init(args); 
    return t; 
} 

然後,您可以使用MakeGenericMethod和/或CreateDelegate


否則;您可以使用Expression(3.5)或DynamicMethod(2.0)進行此操作。

Expression方法更容易代碼:

var param = Expression.Parameter(typeof(int), "val"); 
    var ctor = typeof(Foo).GetConstructor(new[] { typeof(int) }); 
    var lambda = Expression.Lambda<Func<int, Foo>>(
     Expression.New(ctor, param), param); 
    var func = lambda.Compile(); 
    Foo foo = func(123); 
    string s = foo.ToString(); // proof 

或(使用DynamicMethod):

ConstructorInfo ctor = typeof(Foo).GetConstructor(new[] { typeof(int) }); 
    DynamicMethod dm = new DynamicMethod("Create", typeof(Foo), 
      new Type[] { typeof(int) }, typeof(Foo), true); 
    ILGenerator il = dm.GetILGenerator(); 
    il.Emit(OpCodes.Ldarg_0); 
    il.Emit(OpCodes.Newobj, ctor); 
    il.Emit(OpCodes.Ret); 
    Converter<int, Foo> func = (Converter<int, Foo>) 
     dm.CreateDelegate(typeof(Converter<int, Foo>));   
    Foo foo = func(123); 
    string s = foo.ToString(); // proof 
+1

呃哦;從技術上講,使用反射&c。是正確的(我也考慮了一會兒),但它有嚴重的缺陷:1)正如你的評論中所看到的那樣,它會嚴重傷害可讀性(並且使代碼更簡潔而不是更多); 2)AFAIK,反射比語言支持的結構慢,因爲它疊加了更多的抽象層次。 – akavel 2009-10-21 15:59:48

+5

一旦編譯成代表,基於反射的方法不會更慢,並且可以(有時)比常規編譯代碼更快。顯然你只編譯一次並緩存委託。 – 2009-10-21 17:35:39

+3

+1。這個(表達的實現)對我來說比我接受的答案更有幫助,因爲我需要的是cctor而不是ctor。 – 2011-06-17 01:12:08

2

馬克Gravell的答案啓發了我下面的非常簡單的解決方案:

static void Main() 
{ 
    Pet a = _MakeObject(typeof(Dog)); 
    Pet b = _MakeObject(typeof(Cat)); 
} 

private static Pet _MakeObject(Type type) 
{ 
    ConstructorInfo info = type.GetConstructor(new Type[0]); 
    return (Pet)info?.Invoke(null); 
} 

幾乎相同的事情,如果你的構造函數有params(在th例如:1個類型爲int的參數):

static void Main() 
{ 
    Pet a = _MakeObject(typeof(Dog), 5); 
    Pet b = _MakeObject(typeof(Cat), 7); 
} 

private static Pet _MakeObject(Type type, int age) 
{ 
    ConstructorInfo info = type.GetConstructor(new [] { typeof(int) }); 
    return (Pet)info?.Invoke(new object[] { age }); 
} 
相關問題