C#更新集合中每个项目的不同属性

时间:2010-03-09 20:26:49

标签: c# .net .net-3.5

我有这段代码(从实际代码中简化):

public interface IAmount
{
  decimal Amount { get; set; }
}

public class SomeAmount : IAmount
{
  public decimal Amount { get; set; }
}

public static void UpdateAmounts( this IEnumerable< IAmount > amounts, decimal totalAmount )
{
  foreach ( IAmount amount in amounts )
    amount.Amount = GetAmount();      
}

public static decimal GetAmount()
{
  return 12345m;
}

代码运行良好,并且在整个应用程序中经常使用UpdateAmounts ExtensionMethod来应用一分钱舍入例程(不像Office Space中那样!)

问题是我不喜欢IAmount接口具有我需要设置的列的特定名称(Amount)。在新的需求中,我需要使用此例程更新数据库实体集合,并且需要更新的属性的名称是“GrossAmount”。有时也会以类似的方式更新其他可写的小数属性。

问题是看起来我不能简单地说amount.Field = GetAmount()其中.Field部分处理实体上的不同属性。有可能吗?我不在C#4.0上,所以使用动态类型对我来说还不行。

8 个答案:

答案 0 :(得分:1)

如何使用类似字典的界面?

public interface IAmount {

        decimal this[string fieldName] { get; set; }
}

实施简单:

public class Money : IAmout {
    private Dictionary<string, decimal> _dict;

    public decimal this[string fieldName] {
        get { return _dict[fieldName]; }
        set { _dict[fieldName] = value; }
    }
}

(当然,它需要一些错误检查)

然后,可以写:

Money m = new Money();
m["Amount"] = ...

m["GrossAmount"] = ...

不如动态,我同意。

答案 1 :(得分:1)

你可以用更实用的方式做到这一点,如下所示:

    public class Something
    {
        public decimal Amount { get; set; }
        public decimal OtherAmount { get; set; }
    }

    public static void UpdateAmounts<T, U>(IEnumerable<T> items, Action<T,U> setter, Func<T, U> getter)
    {
        foreach (var o in items)
        {
            setter(o, getter(o));
        }
    }

    public void QuickTest()
    {
        var s = new [] { new Something() { Amount = 1, OtherAmount = 11 }, new Something() { Amount = 2, OtherAmount = 22 }};
        UpdateAmounts(s, (o,v) => o.Amount = v, (o) => o.Amount + 1);
        UpdateAmounts(s, (o,v) => o.OtherAmount = v, (o) => o.OtherAmount + 2);
    }

答案 2 :(得分:0)

public class SomeAmount : IAmount
{
  decimal amount;
  public decimal Amount 
  { 
       get{return this.amount;}
       set{this.amount=value; }
  }
}

答案 3 :(得分:0)

不确定你是否愿意使用你的实体,但是......

public class SomeGrossAmount : IAmount
{
    public decimal GrossAmount { get; set; }
    decimal IAmount.Amount
    {
        get { return GrossAmount; }
        set { GrossAmount = value; }
    }   
}

这会将实体的Amount实现隐藏在任何不直接用作IAmount的上下文中,同时仍允许它作为IAmount运行。

答案 4 :(得分:0)

您可以隐藏Field属性,如下所示:

public interface IAmount
{
    decimal Field
    { get; set; }
}

public class SomeAmount : IAmount
{
    public decimal Amount
    { get; set; }

    decimal IAmount.Field
    {
        get { return Amount; }
        set { Amount = value; }
    }
}

public class SomeGrossAmount : IAmount
{
    public decimal GrossAmount
    { get; set; }

    decimal IAmount.Field
    {
        get { return GrossAmount; }
        set { GrossAmount= value; }
    }
}

将对象投射到IAmount会显示Field以达到您的目的。否则,Field隐藏在设计器中,Amount(或GrossAmount)就是您要使用的内容。

答案 5 :(得分:0)

您也可以使用反射来对您的类型中的每个小数点应用舍入。

public static void UpdateAmounts( this IEnumerable< IAmount > amounts, decimal totalAmount )
{
  foreach ( IAmount amount in amounts ) 
  {
     var myType = amount.GetType();
     var myTypeProperties = myType.GetProperties();
     foreach (PropertyInfo h_pi in myTypeProperties) 
     {
        if (h_pi.Property_Type == typeof(decimal)) // or h_pi.Name == "Amount" || h_pi.Name                                      == "GrossAmount"...
        {
           //DoStuff
        }
     }
  }
    amount.Amount = GetAmount();      
}

有更好的方式来写这个,但我相信你明白了。使用反射你也可以摆脱整个界面的东西,只需通过反思。

P.S。 :反射不是最快的方法,但它是一种获得运行时灵活性的简单方法。

请告诉我这是否是您想要的......

答案 6 :(得分:0)

或者,当你不介意使用反射时(它有点慢):它与属性结合非常强大。首先,创建一个用于标记所需十进制属性的属性:

[AttributeUsage(AttributeTargets.Property,
    Inherited = true, AllowMultiple = false)]
sealed class DecimalFieldAttribute : Attribute
{
    public DecimalFieldAttribute()
    { }
}

使用属性标记您的字段,例如:

public class SomeGrossAmount
{
    [DecimalField]
    public decimal GrossAmount
    {
        get;
        set;
    }
}

然后使用此方法设置这样的字段:

public static void SetDecimalField(object obj, decimal value)
{
    // Enumerate through all the properties to find one marked
    // with the DecimalFieldAttribute.
    PropertyInfo[] properties = obj.GetType().GetProperties();
    PropertyInfo decimalfieldproperty = null;
    foreach (PropertyInfo property in properties)
    {
        object[] attributes = property.GetCustomAttributes(typeof(DecimalFieldAttribute), true);
        if (attributes.Length == 0)
            continue;
        // Check, or just break; when you'll not be making this error.
        if (decimalfieldproperty != null)
            throw new Exception("More than one property is marked with the DecimalFieldAttribute.");

        // Found a candidate.
        decimalfieldproperty = property;
    }
    // Check, or just assume that you'll not be making this error.
    if (decimalfieldproperty == null)
        throw new Exception("No property with the DecimalFieldAttribute found.");

    // Set the value.
    decimalfieldproperty.SetValue(obj, value, null);
}

答案 7 :(得分:0)

我会建议这样的事情:

    public class Entity
    {
        public decimal Amount { get; set; }
        public decimal OtherAmount { get; set; }
    }

    public static void Update<TEntity, TValue>(this IEnumerable<TEntity> entities, Func<TValue> valueGetter, Action<TEntity, TValue> valueSetter)
    {
        foreach (TEntity entity in entities)
        {
            TValue value = valueGetter.Invoke();
            valueSetter.Invoke(entity, value);
        }
    }

    public static decimal GetAmount()
    {
        throw new NotImplementedException();
    }

    public static decimal GetOtherAmount()
    {
        throw new NotImplementedException();
    }

    public static IEnumerable<Entity> GetEntities()
    {
        throw new NotImplementedException();
    }

    static void Main()
    {
        IEnumerable<Entity> entities = GetEntities();

        entities.Update<Entity, decimal>(GetAmount, (entity, value) => entity.Amount = value);
        entities.Update<Entity, decimal>(GetOtherAmount, (entity, otherValue) => entity.OtherAmount = otherValue);
    }