C#使用索引设置属性

时间:2010-04-04 14:14:44

标签: c# coding-style properties

我有一个商业类,其中包含各种股票交易价格类型的许多属性。这是该课程的一个示例:

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

我有一个遗留系统,使用字符串ID提供价格来识别价格类型。

E.g。

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

首先,我将所有值加载到IDictionary()集合中,因此我们有:

  

[KEY] VALUE   [0D] => 1.23456
  [1D] => 1.23456
  [6D] => 1.23456
  ....等等。

其次,我使用一个方法设置Price类的属性,该方法将上述集合作为参数,如下所示:

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   
}  

是否有更优雅的技术来设置大量属性? 谢谢, Ĵ

5 个答案:

答案 0 :(得分:2)

使用委托映射/扩展方法,而不是使用字符串到十进制的映射并重复检查字典:

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与任何内容不匹配的情况。

答案 1 :(得分:0)

我会将字符串变量放入常量中,而不是每次运行方法时都声明它们:

private const string ONE_DAY = "D1";

如果您希望collection参数包含所有或大部分可能的值,那么您的代码可能很酷。如果您希望字典具有可能值的一小部分,则使用foreach循环和switch语句来设置值可能更有效,而不是每次都查找每个可能的值。它只取决于您需要处理多少值以及每次方法调用中获得的值。

答案 2 :(得分:0)

我看到它的方式,你有几个选择,取决于你的技能,你被允许改变当前POCO或其他类的方式:

  • 如果必须使用字典,请创建一个类似的字典,将“0D”等映射到OneDay名称。循环遍历字典并使用简单的反射进行分配。
  • 如果您可以更改数据的读取方式,请使用OneDay等读取字典,而不是“0D”,这仅适用于外部应用程序。
  • 创建一个属性LegacyKeyAttribute,使用此属性扩充您的POCO gettors / settors。现在它变得微不足道了:遍历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
            }
        }
    }
}

这就是它的全部。即使添加了所有行,也可能是您的总行数减少了。但更重要的是,维护和使用变得更加容易。

答案 3 :(得分:0)

只是一个想法:

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);
    }
  }
 }
}

然后,当我从遗留系统设置值时,我将使用接口上的Prices类作为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..

HTH。

答案 4 :(得分:0)

在构造函数中定义属性字典,例如

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

稍后您可以通过使用反射自动解析构造函数中的属性来改进此代码, 使用一个属性来添加所有属性,该属性可以告诉您id是什么。 (而不是在构造函数中手动添加它们。)