2016-12-01 213 views
8

我從加載的程序集讀取類型,例如:反思 - 調用構造函數參數

var someType = loadedAssemblies 
      .Where(a => a != null && a.FullName.StartsWith("MY.")) 
      .SelectMany(a => a.GetTypes()) 
      .Distinct() 
      .ToArray()[0]; 

如果counstructor有參數,我能讀懂他們:

ParameterInfo[] parameters = classType.GetConstructors()[0].GetParameters(); 

我想調用默認的構造函數參數值或者如果參數是枚舉,則使用第一個枚舉值。 如果只有一個參數,它是枚舉,它的工作原理是這樣的:

object curObject = Activator.CreateInstance(classType, new object[] { parameters[0].ParameterType.GetEnumValues().GetValue(0) }); 

我怎麼能做到這一點的時候有更多的參數呢? 我需要創建對象讀取屬性:

var propertyInfo = someType.GetProperty("EntityType"); 
string entityType = propertyInfo.GetValue(curObject, null).ToString(); 

回答

5

好了,你可以創建自己的工廠,並編寫方法,即檢查類的構造函數使用默認參數:

public static class MyFactory 
{ 
    public static T MyCreateInstance<T>() 
     where T : class 
    { 
     return (T) MyCreateInstance(typeof (T)); 
    } 

    public static object MyCreateInstance(Type type) 
    { 
     var ctor = type 
      .GetConstructors() 
      .FirstOrDefault(c => c.GetParameters().Length > 0); 

     return ctor != null 
      ? ctor.Invoke 
       (ctor.GetParameters() 
        .Select(p => 
         p.HasDefaultValue? p.DefaultValue : 
         p.ParameterType.IsValueType && Nullable.GetUnderlyingType(p.ParameterType) == null 
          ? Activator.CreateInstance(p.ParameterType) 
          : null 
        ).ToArray() 
       ) 
      : Activator.CreateInstance(type); 
    } 
} 

然後你就可以用這個方法:

var classType = loadedAssemblies 
      .Where(a => a != null && a.FullName.StartsWith("MY.")) 
      .SelectMany(a => a.GetTypes()) 
      .Distinct() 
      .ToArray()[0]; 

var curObject = MyFactory.MyCreateInstance(classType); 

// This will return an array of values 

object[] values = classType 
       .GetFields() 
       .Select(f => f.GetValue(curObject)) 
       .ToArray(); 

P.S. 這是DotNet fiddle example

更新:

的代碼是根據你一起工作的場景改變。現在我們有兩種方法,一種返回對象 ,另一種可以將其轉換爲T型。

我也更新了DotnetFiddle,請檢查它。

+0

嗨,Fabjan,謝謝你的解決方案。有一個問題。 我只有classType,我在運行時讀取。我不知道在編譯時的對象(類)名稱。你的代碼:var curObject = MyFactory.MyCreateInstance (classType);不支持object關鍵字。 – Simon

+0

我已經更新了我的答案以及DotNetfiddle上的代碼示例 – Fabjan

+0

謝謝,它完美無缺。你知道constructor.Invoke與Activator.CreateInstance之間的區別嗎? – Simon

2

你可以做一個輔助方法來獲得一個類型的默認值:

private static object GetDefaultValue(Type type) 
{ 
    if (type.IsEnum) return type.GetEnumValues().GetValue(0); 
    if (type.IsValueType) return Activator.CreateInstance(type); 
    return null; 
} 

然後,你可以得到這些參數的默認值:

var parameters = constructor.GetParameters() 
          .Select(p => GetDefaultValue(p.ParameterType)) 
          .ToArray(); 

,並調用ConstructorInfo來獲得實例:

var obj = constructor.Invoke(parameters); 

如果構造函數的參數有默認值,你要使用它們,你可以做這樣的事情:

var parameters = constructor 
    .GetParameters() 
    .Select(p => p.HasDefaultValue ? p.RawDefaultValue : GetDefaultValue(p.ParameterType)) 
    .ToArray(); 
+0

Arturo,constructor.Invoke與Activator.CreateInstance有什麼區別?你可以用Activator.CreateInstance做同樣的事嗎? – Simon

+0

@Simon:'Constructor.Invoke'直接調用該構造函數。 'Activator.CreateInstance'創建一個實例,查找與指定參數最匹配的構造函數。 –