Назначение полей динамически - PullRequest
1 голос
/ 06 октября 2011

У меня есть набор свойств следующим образом:

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 ]

5 голосов
/ 06 октября 2011

Я думаю, что лучший дизайн будет:

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?

2 голосов
/ 06 октября 2011

Вы можете использовать отражение и делать это в цикле:

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 голос
/ 06 октября 2011

Лично я не согласен с большинством других постеров здесь.Я думаю, что использование отражения должно быть ограничено теми ситуациями, в которых оно действительно требуется (проверка объекта, определенные ситуации с графическим интерфейсом и т. Д.).В этом случае, просто набрав немного больше текста, можно написать программу со строгим набором текста и продолжать делать то, что вы хотите.Я предложу две альтернативы.Обе альтернативы предоставят возможность доступа к вашим свойствам как по имени, так и по индексу.

В первом варианте я предполагаю, что нам разрешено изменять определение ваших свойств.Во втором варианте я предполагаю, что эти определения должны остаться неизменными.

Первый вариант перемещает данные в отдельный массив, добавляет вспомогательные методы для доступа к данным по индексу иизменяет свойства для использования вспомогательных методов:

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
  }
}
1 голос
/ 06 октября 2011

Я бы на самом деле использовал рефлексию или, если это называется много, создал бы динамический метод и ILEmit, чтобы сделать это (намного быстрее во время выполнения, чем рефлексия).

Однако, просто чтобы предложить что-то другое, вы могли быизмените класс, содержащий свойства Foo *, чтобы каждый получатель / установщик считывал из индексированного списка:

  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 , если бы я делал этоя.

1 голос
/ 06 октября 2011
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;
    }
}
1 голос
/ 06 октября 2011

Вам нужно использовать отражение (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);
   }
}
1 голос
/ 06 октября 2011

Вы должны использовать отражение.

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

Хотя две вещи:

  • Стоимость GetProperty не равна нулю.Поэтому, если вы делаете это много раз, вы можете сохранить результат GetProperty в каком-то поле, а затем использовать это поле в вашем foreach.
  • Если ваши свойства действительно называются Something1, Something2 и т. Д...., тогда у вас может быть недостаток дизайна, который вы, возможно, захотите исправить перед этим (замените всех членов строки одним списком).
1 голос
/ 06 октября 2011

Вы можете делать что хотите:

  1. Использование цикла for вместо foreach.Таким образом, вы можете работать с текущим индексом.
  2. Использование отражений.Вы можете получить список свойств для вашего класса и получить к ним динамический доступ.Например, см. Type.GetProperties .

Но почему бы вам просто не использовать List<string> Foos вместо множества свойств?

...