Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/333.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 设置属性,但不知道是哪一个_C#_Generics_Reflection - Fatal编程技术网

C# 设置属性,但不知道是哪一个

C# 设置属性,但不知道是哪一个,c#,generics,reflection,C#,Generics,Reflection,例如,假设我有几个成员参加了这门课(这是一个人为的例子,我不想讨论现实生活中复杂的设计。我真的只想在这里传达一般的想法。): 现在,有3种方法可以创建地址: public void CreateAddressForHouse(); public void CreateAddressForFlat(); public void CreateAddressForSomeOtherBuildingType(); 在表面之下,这组方法做了完全相同的事情,在Address类中设置不同的Id属性。这会在实

例如,假设我有几个成员参加了这门课(这是一个人为的例子,我不想讨论现实生活中复杂的设计。我真的只想在这里传达一般的想法。):

现在,有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);
}
有关
GetPropertyInfo
的实现,请参阅。它获取lambda表达式中指定的成员的
PropertyInfo
(并检查它是否确实是一个属性),您可以使用它在
CreateAddress
方法中设置属性


除此之外,这是一个有效的。也许您不应该使用每个地址类型的属性,而应该使用
字典
属性。这可能可行,也可能不可行,具体取决于类设计及其预期用途。

您可以使用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);
}
有关
GetPropertyInfo
的实现,请参阅。它获取lambda表达式中指定的成员的
PropertyInfo
(并检查它是否确实是一个属性),您可以使用它在
CreateAddress
方法中设置属性


除此之外,这是一个有效的。也许您不应该使用每个地址类型的属性,而应该使用
字典
属性。这可能可行,也可能不可行,取决于类设计及其预期用途。

您可以使用表达式树来简化问题:

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

可以使用表达式树简化问题:

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

您可以按照Corak的建议添加属性BuildingType BuildingType作为enum BuildingType{House,Flat,SomeOtherBuildingType,YetAnotherThing}的值。 为了简化,您可以在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()来生成多种类型的地址。

您可以添加属性BuildingType BuildingType,该属性是Corak建议的enum BuildingType{House,Flat,SomeOtherBuildingType,YetAnotherThing}的值。 为了简化,您可以在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()来生成多种类型的地址。

将方法所做的一切“完全相同”打包到一个单独的方法
CreateAddress()
(可能是
private
)中,然后从这三个方法调用它?或者更好的是,有一个
Guid BuildingTypeID
和一个
BuildingType BuildingType
作为
enum BuildingType{House,Flat,SomethingElse,YetAnotherThing}
的值,这似乎比每种新类型的建筑的新属性都灵活得多。我对每种类型的属性都不满意,这是一些表中有大量可为空的FK的结果:(将方法所做的一切“完全相同”打包到一个单独的方法
CreateAddress()
(可能是
private
)从这三个方法中调用它?或者更好,让一个
Guid BuildingTypeID
和一个
BuildingType BuildingType
作为
enum BuildingType{House,Flat,SomethingElse,YetAnotherThing}的值
,对于每种新类型的建筑,它似乎比新属性灵活得多。我对每种类型的属性都不满意,这是因为有些表中有许多可为空的FK:(当使用
a=>new Guid()调用时,将抛出此(
(MemberExpression)idPropertySelector.Body
)(或者基本上不是MemberExpression的任何内容)@CodeCaster有时我觉得答案不应该适合所有的用例,也不应该关注所有的副作用。归根结底,我们不是一个编码服务……如果OP得到了这个想法,那就足够了。你的意见是什么?你完全正确。我们不必提供傻瓜式的、可复制的粘贴代码。我只是把它作为一个警告来评论。这是code不是用户友好的,也不应该按原样使用(至少,如果您在错误使用代码时关心(重新)可用性和相关异常)。但这适用于大多数代码,所以::-)这个表达式api非常好。应该研究一下:)@CodeCaster认为防弹代码应该放在某个GitHub存储库中,或者谁知道呢。至少对我来说,一天结束时,还有一些想法可以帮助我了解更多关于某个主题的方向。我不喜欢提供包含所有细节的完整解决方案,因为你需要一些挑战来超越你自己的知识和技能,然后复制粘贴解决方案不适用于此(
(MemberExpression)idPropertySelector.Body
)在使用
a=>new Guid()
(或者基本上不是MemberExpression的任何内容)调用时将抛出@CodeCaster有时我觉得答案不应该适合所有的用例,也不应该关注所有的副作用。归根结底,我们不是一个编码服务……如果OP得到了这个想法,那就足够了。你的意见是什么?你完全正确。我们不必提供傻瓜式的、可复制的粘贴代码。我只是把它作为一个警告来评论。这是code不是用户友好的,也不应该按原样使用(至少,如果您在错误使用代码时关心(重新)可用性和相关异常),但这适用于大多数代码,所以::-)这个表达式api非常好。应该研究以下内容:)@CodeCaster防弹代码