动态分配字段

本文关键字:字段 动态分配 | 更新日期: 2023-09-27 18:10:39

我设置了如下属性:

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.
}

是否有简单的方法来做到这一点?

动态分配字段

我认为更好的设计应该是:

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中的值确定索引吗?

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

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

你可以这样做:

  1. 使用for循环代替foreach。这样就可以使用当前索引进行操作。
  2. 使用反射。您可以获取类的属性列表并动态访问它们。例如:Type.GetProperties。

但是你为什么不直接使用一个List<string> Foos而不是很多属性呢?

你应该使用反射。

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

  • GetProperty的开销不为空。如果你经常这样做,你可能想把GetProperty的结果存储在某个字段中然后在foreach中使用这个字段。
  • 如果你的属性真的被命名为Something1, Something2,等等…,那么你可能有一个设计缺陷,你可能想在这样做之前纠正(用一个列表替换你所有的字符串成员)。

您需要使用反射(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);
   }
}
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;
    }
}

我实际上会使用反射,或者如果这被调用很多,做一个动态方法和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,而不是通过每个单独的属性。

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

就我个人而言,我不同意这里大多数其他海报。我认为反射的使用应该限制在那些真正需要它的情况下(对象检查,特定的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
  }
}