一尘不染

是否可以访问自动实现属性后面的后备字段?

c#

我知道我可以对属性使用详细语法:

private string _postalCode;

public string PostalCode
{
    get { return _postalCode; }
    set { _postalCode = value; }
}

或者,我可以使用自动实现的属性。

public string PostalCode { get; set; }

我可以以某种方式访问​​自动实现属性后面的支持字段吗?(在此示例中为_ postalCode )。


编辑 :我的问题不是关于设计,而是关于设计的理论能力。


阅读 407

收藏
2020-05-19

共1个答案

一尘不染

我不了解您,但是我已经在其他公司的项目中编写了代码,现在我想知道我是如何做的!因此,通常在网络上搜索答案会更快,这使我到了这里。

但是,我的原因有所不同。我正在进行单元测试,不在乎纯粹主义者要说什么,但是作为单元测试设置的一部分,我试图为给定对象调用特定状态。但是该状态应在内部进行控制。我不希望其他开发人员不小心弄乱状态,而这可能会对系统产生深远的影响。因此它必须是私下设置的!但是,如何在不调用(希望)永远不会发生的行为的情况下对这样的事情进行单元测试?在这种情况下,我认为在单元测试中使用反射是有用的。

另一种方法是公开我们不希望公开的内容,因此我们可以对它们进行单元测试!是的,我已经在现实生活环境中看到了这一点,只是思考它仍然让我摇头。

因此,我希望下面的代码可能有用。

实际上,这里有两种方法用于分离关注点,也有助于提高可读性。对于大多数开发人员而言,反射是令人费解的事情,以我的经验,他们要么回避它,要么像瘟疫一样避开它!

private string _getBackingFieldName(string propertyName)
{
    return string.Format("<{0}>k__BackingField", propertyName);
}

private FieldInfo _getBackingField(object obj, string propertyName)
{
    return obj.GetType().GetField(_getBackingFieldName(propertyName), BindingFlags.Instance | BindingFlags.NonPublic);
}

我不知道您要使用哪种代码约定,但就我个人而言,我喜欢将助手方法设为私有并以小写字母开头。我在阅读时觉得不够明显,所以我也喜欢前面的下划线。

讨论了后备字段及其自动命名。为了进行单元测试,您将很快知道它是否已更改!它也不会对您的真实代码造成灾难性的影响,只是对测试而言。因此,我们可以对名称的命名做出简单的假设,就像我上面提到的那样。您可能不同意,这很好。

更加困难的助手_getBackingField将返回这些反射类型之一FieldInfo。我在这里也做出了一个假设,即您所追求的支持字段来自于作为实例的对象,而不是静态的。您可以根据需要将其分解为要传递的参数,但是对于那些可能需要功能而不是了解的普通开发人员而言,无疑会更加困惑。

关于FieldInfos 的方便之处在于,他们可以在与匹配的对象上设置字段FieldInfo。用一个例子可以更好地解释:

var field = _getBackingField(myObjectToChange, "State");
field.SetValue(myObjectToChange, ObjectState.Active);

在这种情况下,该字段是称为的枚举类型ObjectState。名称已更改,以保护无辜者!因此,在第二行中,您可以看到,通过访问FieldInfo先前返回的内容,我可以调用该SetValue方法,您可能认为该方法应该已经与您的对象相关联,但与您的对象无关!这就是反射的本质-
FieldInfo将字段与它的来源区分开来,因此您必须告诉它要与(myObjectToChange)一起使用的实例,并在此情况下告诉您希望它具有的值ObjectState.Active

长话短说,面向对象的编程将阻止我们执行诸如访问私有字段之类的令人讨厌的事情,更糟糕的是,在代码开发人员无意时更改它们。哪个好!这就是C#如此有价值并被开发人员喜欢的原因之一。

但是,微软给了我们反思,并且通过它,我们挥舞了强大的武器。它可能很丑陋,而且很慢,但同时,它也暴露了MSIL(简称MicroIL)的内部工作的最深层次,简称IL,并使我们能够几乎打破书中的每条规则,是一个很好的例子。

2020-05-19