我有这段代码(从实际代码中简化):
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上,所以使用动态类型对我来说还不行。
答案 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);
}