2013-03-04 85 views
0

我有一個C#轉換器方法,它將通用列表與使用反射進行對話。如何在反射中使用索引屬性的SetValue?

的問題,當我嘗試調用該項目的財產SetValue方法時,它拋出以下內部異常(ArgumentOutOfRangeException):

指數超出範圍。必須爲 非負數且小於集合的大小。參數名稱: 索引。

這裏是我的代碼:

internal class Program 
{ 
    private static void Main() 
    { 
     List<ClassA> classA = new List<ClassA>(); 
     classA.Add(new ClassA { Data = "value1" }); 
     classA.Add(new ClassA { Data = "value2" }); 

     List<ClassB> classB = Converter<List<ClassA>, List<ClassB>>(classA); 
    } 

    public static TOut Converter<TIn, TOut>(TIn request) 
    { 
     var response = Activator.CreateInstance<TOut>(); 
     PropertyInfo propertyA = typeof(TIn).GetProperty("Item"); 
     PropertyInfo propertyB = typeof(TOut).GetProperty("Item"); 

     int count = (int)typeof(TIn).GetProperty("Count").GetValue(request); 
     for (int i = 0; i < count; i++) 
     { 
      var value = propertyA.GetValue(request, new object[] { i }); 
      var b = CreateBFromA(propertyB, propertyA, value); 
      propertyB.SetValue(response, b, new object[] { i }); 
     } 

     return response; 
    } 

    private static object CreateBFromA(PropertyInfo propertyB, PropertyInfo propertyA, object value) 
    { 
     var b = Activator.CreateInstance(propertyB.PropertyType); 
     object o = propertyA.PropertyType.GetProperty("Data").GetValue(value); 
     propertyB.PropertyType.GetProperty("Data").SetValue(b, o); 
     return b; 
    } 
} 

internal class ClassA 
{ 
    public string Data { get; set; } 
} 

internal class ClassB 
{ 
    public string Data { get; set; } 

    public object Other { get; set; } 
} 

這是一個更大的泛型方法(在這裏我需要使用反射)的一個小例子代碼,所以你可以嘗試並運行它重新生成異常。

如何使用SetValue方法避免此異常?

+2

由於您使用List 而不是Array,因此您應該先將對象添加到集合中,然後才能使用索引器。可能執行'typeof(TOut).GetMethod(「Add」)。Invoke(response,new object [] {null});'在'propertyB.SetValue(response,b,new object [] {i})之前''會幫幫我。但我沒有編譯這個,只是猜測。 – 2013-03-04 14:05:36

+0

請原諒我的問題;但是這不會更容易*通過抽象/接口實現? – Richard 2013-03-04 14:20:16

+0

@Marek Kembrowski是的,你是對的。我只能將那些已經添加的元素(在這種情況下)設置爲集合。 – ZYD 2013-03-04 15:26:42

回答

1

這是我的aproach它:

public static TCollectionOut ConvertCollection<TCollectionIn, TCollectionOut, TIn, TOut>(TCollectionIn input) 
     where TCollectionIn : IEnumerable<TIn> 
     where TCollectionOut : ICollection<TOut>, new() 
     where TOut : new() 
    { 
     var res = new TCollectionOut(); 

     foreach (dynamic item in input) 
     { 
      dynamic o = new TOut(); 
      ConvertItem(item, o); 
      res.Add(o); 
     } 
     return res; 
    } 

    public static TCollectionOut ConvertCollectionMoreDynamic<TCollectionIn, TCollectionOut>(TCollectionIn input) 
     where TCollectionIn : IEnumerable 

    { 
     dynamic res = Activator.CreateInstance(typeof (TCollectionOut)); 

     var oType = typeof (TCollectionOut).GetMethod("Add").GetParameters().Last().ParameterType; 

     foreach (dynamic item in input) 
     { 
      dynamic o = Activator.CreateInstance(oType); 
      ConvertItem(item, o); 
      res.Add(o); 
     } 
     return res; 
    } 

    public static void ConvertItem(ClassA input, ClassB output) 
    { 
     output.Data = input.Data; 
    } 

如果您至極,以支持更多類型的只是創建ConvertItem方法與正確的過載。

0

這是因爲您試圖將索引傳遞給未索引的屬性(Data)。

如果您發佈了ClassA代碼,我可以嘗試喲幫助。無論如何,你可以使用LINQ來執行這種轉換。它更快(編寫和執行)並鍵入安全。