在不知道是哪个属性时设置属性

本文关键字:属性 设置 不知道 | 更新日期: 2023-09-27 18:09:43

假设这个类有几个成员,例如(这是一个人为的例子,我宁愿不讨论实际设计的复杂性)。我真的只是想在这里传达一个大致的想法。

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()来生成多种类型的地址