C # Установка свойств с помощью индекса - PullRequest
2 голосов
/ 04 апреля 2010

У меня есть бизнес-класс, который содержит много свойств для различных типов биржевых цен. Это образец класса:

public class Prices
{
    public decimal Today {get; set;}
    public decimal OneDay {get; set;}
    public decimal SixDay {get; set;}
    public decimal TenDay {get; set;}
    public decimal TwelveDay {get; set;}
    public decimal OneDayAdjusted {get; set;}
    public decimal SixDayAdjusted {get; set;}
    public decimal TenDayAdjusted {get; set;}
    public decimal OneHundredDayAdjusted {get; set;}
}

У меня есть устаревшая система, которая предоставляет цены, используя строковые идентификаторы для определения типа цены.

например.

Today = "0D"  
OneDay = "1D"  
SixDay = "6D"  
//..., etc.   

Во-первых, я загружаю все значения в коллекцию IDictionary (), поэтому мы имеем:

[КЛЮЧ] ЗНАЧЕНИЕ
[0D] => 1,23456
[1D] => 1,23456
[6D] => 1,23456
.... и т. д.

Во-вторых, я установил свойства класса «Цены», используя метод, который принимает вышеуказанную коллекцию в качестве параметра, например так:

SetPricesValues(IDictionary<string, decimal> pricesDictionary)  
{  
    // TODAY'S PRICE  
    string TODAY = "D0";  
    if (true == pricesDictionary.ContainsKey(TODAY))  
    {  
        this.Today = pricesDictionary[TODAY];  
    }  
    // OneDay PRICE  
    string ONE_DAY = "D1";  
    if (true == pricesDictionary.ContainsKey(ONE_DAY))  
    {  
         this.OneDay = pricesDictionary[ONE_DAY];  
    }  
//..., ..., etc., for each other property   
}  

Есть ли более элегантная техника для установки большого количества свойств? Спасибо, J

Ответы [ 5 ]

2 голосов
/ 04 апреля 2010

Вместо использования преобразования строки в десятичную систему и многократной проверки словаря используйте метод отображения / расширения делегата:

public static class PriceConverter
{
    private static readonly Dictionary<string, Action<Prices, decimal>> setters =
        CreateSetterDictionary();

    public static void SetPrice(this Prices p, string id, decimal newPrice)
    {
        Action<Prices, decimal> setter;
        if (setters.TryGetValue(id, out setter))
            setter(p, newPrice);
    }

    private static Dictionary<string, Action<Prices, decimal>>
        CreateSetterDictionary()
    {
        var dic = new Dictionary<string, Action<Prices, decimal>>();
        dic.Add("0D", (p, d) => p.Today = d);
        dic.Add("1D", (p, d) => p.OneDay = d);
        // etc.
        return dic;
    }
}

Тогда вы можете написать prices.SetPrice("0D", 1.23456).

Если хотите, добавьте оператор throw в конце метода SetPrice для обработки случаев, когда id ничего не соответствует.

0 голосов
/ 04 апреля 2010

Определить словарь свойств в конструкторе, например.

private Dictionary<int, PropertyInfo> propertyDictionary = new ...

MyClass()
{
    this.propertyDictionary.Add(0, this.GetType().GetProperty("FirstProperty");
    ...
}

затем получить доступ с помощью индексированного свойства

decimal this[int index]
{
    get
    {
        PropertyInfo property;
        if (this.propertyDictionary.TryGetValue(index, out property))
        {
            // Not sure I remember the arguments right here:
            property.SetValue(this, new object[] { value });
        }
    set
    {
        // Similar code
    }
}

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

0 голосов
/ 04 апреля 2010

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

  • Если вы должны использовать словарь, создайте аналогичный словарь, который сопоставляет «0D» и т. Д. С именами OneDay. Переберите словарь и назначьте, используя простое отражение.
  • Если вы можете изменить способ чтения данных, прочитайте словарь с помощью OneDay и т. Д. Вместо «0D», который применим только к внешнему приложению.
  • Создайте атрибут, LegacyKeyAttribute, увеличьте ваши получатели / установщики POCO этим атрибутом. Теперь это становится тривиальным: просматривайте свойства POCO, чтобы найти правильное свойство для вашего текущего унаследованного ключа.

Последний вариант требует немного большего понимания C #, чем многие обычные программисты знают: написание и использование атрибутов и рефлексии. Однако, в конце концов, это самое чистое и простое решение (я постараюсь привести пример).


ОБНОВЛЕНИЕ: вот небольшой пример. Между тем, было опубликовано много предложений по улучшению, но ни одно из них по-прежнему не использует атрибуты, в то время как ваш случай кажется идеальным. Зачем? Я считаю, что это создает наименьшую нагрузку на существующий код и делает чтение и понимание вашего кода еще проще.

Использование:

// any price:
Prices prices = new Prices();
prices.SetPriceByLegacyName("0D", 1.2345M);

// or, your loop becomes a bit easier:
SetPricesValues(IDictionary<string, decimal> pricesDictionary)  
{  
    foreach(string key in pricesDictionary.Keys)
    {
        // assuming "this" is of type Prices (you didn't specify)
        this.SetPriceByLegacyName(key, pricesDictionary[key]);
    }    
}  

Реализация:

// the simplest attribute class is enough for you:
[AttributeUsage(AttributeTargets.Property)]
public class LegacyNameAttribute : Attribute
{
    public string Name { get; set; }
    public LegacyNameAttribute(string name)
    {
        this.Name = name;
    }
}

// your Prices POCO class becomes easier to read
public class Prices
{
    [LegacyName("0D")]    public decimal Today { get; set; }
    [LegacyName("1D")]    public decimal OneDay { get; set; }
    [LegacyName("6D")]    public decimal SixDay { get; set; }
    [LegacyName("10D")]   public decimal TenDay { get; set; }
    [LegacyName("12D")]   public decimal TwelveDay { get; set; }
    [LegacyName("1DA")]   public decimal OneDayAdjusted { get; set; }
    [LegacyName("6DA")]   public decimal SixDayAdjusted { get; set; }
    [LegacyName("10DA")]  public decimal TenDayAdjusted { get; set; }
    [LegacyName("100DA")] public decimal OneHundredDayAdjusted { get; set; }
}

// an extension method to ease the implementation:
public static class PricesExtensions
{
    public static void SetPriceByLegacyName(this Prices price, string name, decimal value)
    {
        if (price == null)
            throw new ArgumentException("Price cannot be null");

        foreach (PropertyInfo prop in price.GetType().GetProperties())
        {
            LegacyNameAttribute legNameAttribute = (LegacyNameAttribute)
                Attribute.GetCustomAttribute(prop, typeof(LegacyNameAttribute));

            // set the property if the attribute matches
            if (legNameAttribute != null && legNameAttribute.Name == name)
            {
                prop.SetValue(price, value, null);
                break;   // nothing more to do
            }
        }
    }
}

Вот и все, что нужно сделать. Даже со всеми добавленными строками вполне может быть, что общее количество строк становится меньше. Но что более важно, его становится проще поддерживать и использовать.

0 голосов
/ 04 апреля 2010

Просто идея:

interface IPrices_As_String{
 string OD { get; set; }
 // other properties here...
}

interface IPrices{
 decimal Today{get; set;}
}

class Prices : IPrices, IPrices_As_String{
 public decimal Today { get; set; }
 public string IPrices_As_String.OD {
  get { return this.Today.ToString(); }
  set { 
    if(!String.IsNullOrEmpty(value)){
       this.Today = decimal.Parse(value);
    }
  }
 }
}

Затем, когда я устанавливаю значения из прежней системы, я буду использовать класс Цены в интерфейсе как IPrices_As_String как:

IPrices_As_String obj = new Prices();
// set values from the legacy system

IPrices obj2 = obj as IPrices; // will give me the correct object..

.

НТН.

0 голосов
/ 04 апреля 2010

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

private const string ONE_DAY = "D1";

Если вы ожидаете, что параметр коллекции будет содержать все или большинство возможных значений, то вашкод наверное классный.Если вы ожидаете, что в словаре будет небольшое подмножество возможных значений, может быть более эффективно использовать цикл foreach и инструкцию switch для установки значений, а не выполнять поиск каждого возможного значения каждый раз.Это зависит только от того, сколько значений вам нужно иметь в виду и сколько вы получаете при каждом вызове метода.

...