动态分配字段

时间:2011-10-06 14:27:43

标签: c# .net reflection collections

我有如下属性集:

public string Foo1 {set;get;}
public string Foo2 {set;get;}
public string Foo3 {set;get;}
public string Foo4 {set;get;}
public string Foo5 {set;get;}
public string Foo6 {set;get;}
public string Foo7 {set;get;}
public string Foo8 {set;get;}
public string Foo9 {set;get;}
......
public string Foo50 {set;get;}

然后我按如下方式遍历集合:

foreach(var element in sortedCollection.Keys){
   if(element != null)
   // in this block I would like to assign the element to the properties above
   // ex:
   foo?? = sortedCollection[element];
   // ?? need to be replaced by index.
}

有一种简单的方法吗?

8 个答案:

答案 0 :(得分:5)

我认为更好的设计是:

public List<string> Foos { get; private set; }

如果你无法改变它,你可能会做类似的事情:

var type = typeof(MyCalss);
int index = 1;
foreach (var key in sortedCollection.Keys)
{
   var value = sortedCollection[key];
   var prop = type.GetProperty("Foo" + index);
   if (prop != null) prop.SetValue(this, value, null);

   index++;
}

...当然有一些错误处理,this假设这是你班级中的方法。您能否根据sortedCollection

中的值确定索引

答案 1 :(得分:2)

你可以使用反射并在循环中执行:

for ( int i = 1 ; i < 51 ; i++)
  this.GetType().GetProperty(string.Format("Foo{0}",i)).SetValue(this,desiredValue,null);

但我想我会推荐使用索引器

http://msdn.microsoft.com/en-us/library/2549tw02%28v=vs.80%29.aspx

答案 2 :(得分:1)

你可以做你想做的事:

  1. 使用for循环而不是foreach。这样您就可以使用当前索引进行操作。
  2. 使用反射。您可以获取类的属性列表并动态访问它们。例如,请参阅Type.GetProperties
  3. 但为什么不用List<string> Foos代替很多属性?

答案 3 :(得分:1)

你应该使用反射。

this.GetType()。GetProperty(“Foo”+ i)。SetValue(this,sortedCollection [element],null);

但有两件事:

  • GetProperty的费用不为空。因此,如果您这样做很多次,您可能希望将GetProperty的结果存储在某个字段中,然后在foreach中使用此字段。
  • 如果您的属性确实名为Something1,Something2等等,那么在执行此操作之前您可能有一个设计缺陷可能需要更正(将所有字符串成员替换为一个List)。

答案 4 :(得分:1)

您需要使用反射(Type.GetProperty())来获取属性并设置它的值。

假设属性在名为MyClass的类中定义:

foreach(var element in sortedCollection.Keys){
   if(element != null)
   // in this block I would like to assign the element to the properties above
   // ex:
   //foo?? = sortedCollection[element];
   // not sure how you are getting the index here, may be you need to use for loop
   PropertyInfo pi = typeof(MyClass).GetProperty("Foo" + index);

   // ?? need to be replaced by index.
   if (pi != null)
   {
       pi.SetValue(<object of MyClass>, sortedCollection[element], null);
   }
}

答案 5 :(得分:1)

void Main()
{
    var foo = new Foo();
    foo[1] = "Foo1";    
    //foo.Dump();
}

public class Foo
{
    public string Foo1 {set;get;}
    public string Foo2 {set;get;}
    public string Foo3 {set;get;}
    public string Foo4 {set;get;}
    public string Foo5 {set;get;}
    public string Foo6 {set;get;}
    public string Foo7 {set;get;}
    public string Foo8 {set;get;}
    public string Foo9 {set;get;}

    public string this[int index]
    {
        get
        {
            return getPropertyValue(index);
        }
        set
        {
            setPropertyValue(index, value);
        }
    }
    private void setPropertyValue(int i, string value)
    {
       var propi = this.GetType().GetProperty("Foo" + i);
       if (propi != null)
          propi.SetValue(this,value,null);
    }
    private string getPropertyValue(int i)
    {
        var propi = this.GetType().GetProperty("Foo" + i);
        if (propi != null)
            return (string)propi.GetValue(this, null);
        return null;
    }
}

答案 6 :(得分:1)

我实际上会使用反射,或者如果调用它很多,请创建一个动态方法并且ILEmit执行它(在运行时比反射快得多)。

然而,只是为了提出不同的建议,您可以更改包含Foo *属性的类,以便从索引列表中读取每个getter / setter:

  public class FooOfDoom
  {
    public string[] Foos = new string[2];

    public string Foo1
    {
      set { Foos[0] = value; }
      get { return Foos[0]; }
    }

    public string Foo2
    {
      set { Foos[1] = value; }
      get { return Foos[1]; }
    }

  }

然后你的类并没有真正改变,因为它与所有其他代码的合同,因为属性仍然存在,但现在你可以分配到Foos而不是通过每个单独的属性。< / p>

同样,实际上如果我自己做的话,我实际上会使用DynamicMethod

答案 7 :(得分:1)

就个人而言,我不同意这里的大多数其他海报。我认为反射的使用应限于那些真正需要它的情况(对象检查,某些GUI情况等)。在这种情况下,只需更多一点打字,就可以编写一个强类型的程序,仍然可以做你想要的。我会提供两种选择。两种选择都可以按名称和索引访问您的媒体资源。

在第一种选择中,我假设我们可以更改属性的定义。在第二种选择中,我将假设这些定义必须保持不变。

第一种方法是将数据移动到一个单独的数组中,添加辅助方法以通过索引访问数据,并改变属性以使用辅助方法:

private class Version1 {
  private readonly string[] underlyingData=new string[50];

  public string Foo1 { get { return ReadFoo(1); } set { SetFoo(1, value); } }
  public string Foo2 { get { return ReadFoo(2); } set { SetFoo(2, value); } }
  public string Foo3 { get { return ReadFoo(3); } set { SetFoo(3, value); } }
  //......
  public string Foo50 { get { return ReadFoo(50); } set { SetFoo(50, value); } }

  private string ReadFoo(int index) {
    return underlyingData[index-1]; //1-based indexing
  }

  private void SetFoo(int index, string value) {
    underlyingData[index-1]=value; //1-based indexing
  }
}

第二种方法是保持属性定义不变,两个静态的委托数组表示这些属性的读写功能。

private class Version2 {
  private static readonly Func<Version2, string>[] readers=new Func<Version2, string>[] {
    c => c.Foo1,
    c => c.Foo2,
    c => c.Foo3,
    //......
    c => c.Foo50,
  };

  private static readonly Action<Version2, string>[] writers=new Action<Version2, string>[] {
    (c,v) => c.Foo1=v,
    (c,v) => c.Foo2=v,
    (c,v) => c.Foo3=v,
    //......
    (c,v) => c.Foo50=v,
  };

  public string Foo1 { set; get; }
  public string Foo2 { set; get; }
  public string Foo3 { set; get; }
  //......
  public string Foo50 { set; get; }

  private string ReadFoo(int index) {
    return readers[index-1](this); //1-based indexing
  }

  private void SetFoo(int index, string value) {
    writers[index-1](this, value); //1-based indexing
  }
}