假设这个类有几个成员,例如(这是一个人为的例子,我宁愿不讨论实际设计的复杂性)。我真的只是想在这里传达一个大致的想法。
public class Address
{
public Guid Id { get; set; }
public Guid? HouseId { get; set; }
public Guid? FlatId { get; set; }
public Guid? SomeOtherBuildingTypeId { get; set;
}
现在正好有3种方法来创建一个地址:
public void CreateAddressForHouse();
public void CreateAddressForFlat();
public void CreateAddressForSomeOtherBuildingType();
在表面之下,这组方法做了完全相同的事情,只是在Address类中设置了一个不同的Id属性。这在实际应用程序中导致了相当多的代码重复,我想把它重写成更通用的东西。
在我的脑海中,我可以传递所需属性的名称及其值给CreateAddress函数,在类似Func的东西中。但我在这方面真的很欠缺,从哪里开始呢?我可以使用什么现成的。net东西?或者我应该寻找哪些特定的关键字?
您可以使用MemberExpression:
public void CreateAddress(Expression<Func<Address, Guid?>> member)
{
// Get the property from the expression
var propertyInfo = GetPropertyInfo(this, member);
// Create a new address
var guid = Guid.NewGuid();
// Assign it to the property of this instance
propertyInfo.SetValue(this, guid);
}
然后像这样调用方法,使用lambda a => a.PropertyName
:
var address = new Address();
address.CreateAddress(a => a.HouseId);
Console.WriteLine(address.HouseId);
参见从lambda表达式中检索属性名了解GetPropertyInfo
的实现。它获得在lambda表达式中指定的成员的PropertyInfo
(并检查它是否确实是一个属性),您可以使用CreateAddress
方法设置该属性。
除此之外,@Corak的建议是有效的。也许你不应该为每个地址类型使用一个属性,而是使用Dictionary<AddressType, Guid?>
属性。这可能是可行的,也可能是不可行的,这取决于类的设计和它的预期用途。
你可以使用表达式树来简化你的问题:
public class AddressService
{
public Address CreateAddress(Expression<Func<Address, Guid?>> idPropertySelector)
{
// So you get the property info to later set it using reflection
MemberExpression propertyExpr = (MemberExpression)idPropertySelector.Body;
PropertyInfo property = (PropertyInfo)propertyExpr.Member;
// Then you create an instance of address...
Address address = new Address();
// and you set the property using reflection:
property.SetValue(address, (Guid?)Guid.NewGuid());
return address;
}
}
现在,谁知道在你的代码,这将工作:
AddressService service = new AddressService();
Address address = service.CreateAddress(a => a.FlatId);
Guid? flatId = address.FlatId; // This will be already assigned!
您可以添加属性BuildingType BuildingType作为enum BuildingType {House, Flat, SomeOtherBuildingType, YetAnotherThing}的值,这是Corak建议的。为了简化,您可以在Address类中创建一个参数化的构造函数:
public Address(Guid? id,BuildingType type)
{
switch(type)
{
case BuildingType.House:
HouseId=id;
break;
case BuildingType.Flat:
FlatId=id;
break;
case BuildingType.SomeOtherBuildingType:
SomeOtherBuildingTypeId =id;
break;
default:
break;
}
}
这样更容易扩展。此外,您不需要有那么多方法。只能使用一个CreateAddress()来生成多种类型的地址