#c# #.net #reflection #collections
#c# #.net #отражение #Коллекции
Вопрос:
У меня есть набор свойств следующим образом:
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.
}
Есть ли простой способ сделать это?
Комментарии:
1. Есть ли причина для такого дизайна класса?
2. он уже разработан таким образом. не могу это изменить.
3. Вероятно, позже вы столкнетесь с большими проблемами, если будете использовать рефлексию, а не рефакторинг для улучшения дизайна… (После добавления отражения становится очень сложно его реорганизовать / перепроектировать)
Ответ №1:
Я думаю, что лучший дизайн был бы:
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. Ну, в качестве обходного пути вы могли бы создать индексатор, который использовал бы отражения для доступа к свойствам Foo . Это, по крайней мере, выглядело бы лучше.
2. @user177883: Вы должны возразить тому, кто говорит, что вы не можете улучшить дизайн — исправление плохого дизайна, подобного этому, почти всегда лучше делать раньше, а не позже.
3. Как я уже упоминал, я не могу изменить дизайн. и мне это тоже не нравится. вздох.
Ответ №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(v=vs.80).aspx
Ответ №3:
Вы можете делать то, что хотите, с помощью:
- Использование цикла for вместо foreach . Таким образом, вы можете работать с текущим индексом.
- Использование отражений. Вы можете получить список свойств для своего класса и получить к ним доступ динамически. Например, см. раздел Тип.Получить свойства.
Но почему бы вам просто не использовать a List<string> Foos
вместо множества свойств?
Ответ №4:
Вы должны использовать отражение.
это.GetType().getProperty(«Foo» i).setValue(это, SortedCollection[элемент], null);
Однако есть две вещи:
- Стоимость getProperty не равна нулю. Поэтому, если вы делаете это много раз, вы можете сохранить результат getProperty в каком-либо поле, а затем использовать это поле в вашем foreach .
- Если ваши свойства действительно называются Something1, Something2 и т. Д., То у вас может быть недостаток дизайна, который вы, возможно, захотите исправить, прежде чем делать это (замените все ваши строковые элементы одним списком).
Ответ №5:
Вам нужно использовать отражение (тип.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);
}
}
Ответ №6:
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;
}
}
Ответ №7:
Я бы на самом деле использовал отражение, или, если это часто вызывается, создал динамический метод и научился делать это (намного быстрее во время выполнения, чем отражение).
Однако, чтобы предложить что-то другое, вы могли бы изменить класс, содержащий свойства 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, если бы делал это сам.
Ответ №8:
Лично я не согласен с большинством других плакатов здесь. Я думаю, что использование отражения должно быть ограничено теми ситуациями, когда это действительно требуется (проверка объектов, определенные ситуации с графическим интерфейсом и т. Д.). В этом случае, набрав немного больше текста, можно написать строго типизированную программу и при этом делать то, что вы хотите. Я предложу две альтернативы. Обе альтернативы обеспечат возможность доступа к вашим свойствам как по имени, так и по индексу.
В первом варианте я предполагаю, что нам разрешено изменять определение ваших свойств. Во втором варианте я предполагаю, что эти определения должны оставаться неизменными.
Первый вариант перемещает данные в отдельный массив, добавляет вспомогательные методы для доступа к данным по индексу и изменяет свойства для использования вспомогательных методов:
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
}
}