繁体   English   中英

如何使用反射获取私有字段的值?

[英]How to get the value of private field using reflection?

我遇到了一个问题,我需要访问类的私有字段。 例如:

class MyClass 
{
    private string someString;

    public MyClass( string someStringValue )
    {
        someString = someStringValue;
    }
}

如何在 MyClass 之外获取 someString 的值?

更新:

抱歉,我不能在这里使用属性,因为实际的生产代码是受保护的。 我是一名 QA/Dev,我需要一种方法来将这些私有化以编写用户验收测试。 所以我不能更改生产代码。 你能帮我吗?

正如其他人所说,由于该字段是私有的,因此您不应该尝试使用普通代码来获取它。 唯一可以接受的情况是在单元测试期间,即便如此,您也需要一个很好的理由来这样做(例如将私有变量设置为 null,以便异常块中的代码将被命中并可以进行测试)。

您可以使用类似下面的方法来获取字段:

/// <summary>
/// Uses reflection to get the field value from an object.
/// </summary>
///
/// <param name="type">The instance type.</param>
/// <param name="instance">The instance object.</param>
/// <param name="fieldName">The field's name which is to be fetched.</param>
///
/// <returns>The field value from the object.</returns>
internal static object GetInstanceField(Type type, object instance, string fieldName)
{
    BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
        | BindingFlags.Static;
    FieldInfo field = type.GetField(fieldName, bindFlags);
    return field.GetValue(instance);
}

所以你可以这样称呼它:

string str = GetInstanceField(typeof(YourClass), instance, "someString") as string;

同样,这不应在大多数情况下使用。

你不能——你也不是故意的。 这是私人的 如果这是其他人的课程,那么显然他们不希望您访问该领域。 它是私有的这一事实允许他们稍后更改实现 - 他们可能最终将该值作为另一个变量的一部分,或者重命名,或者如果不再需要它来实现公共 API,则可能完全消失。

如果它是您自己的类并且您确定希望其他人能够访问它,只需使用属性公开它:

public string SomeString { get { return someString; } }

编辑:看到您的评论后,您可以通过反射访问私有字段……但是对于验收测试,您不必这样做。 您应该测试公共 API。 对于单元测试,有时弯曲规则是有意义的,并将类视为“白盒”而不是“黑盒”测试,但对于验收测试,我肯定会坚持使用公共 API。

如果这没有帮助,我建议您与生产代码的开发人员交谈:解释您为什么要访问,并要求他们通过属性公开它。 他们可以将其设为内部属性,并使用[InternalsVisibleTo]在您的测试程序集中访问它。 我个人更喜欢使用反射 - 否则,如果生产代码以完全有效的方式更改,您的测试将在不应该失败时失败。

我遇到一个问题,我需要访问班级的私有领域。 例如:

class MyClass 
{
    private string someString;

    public MyClass( string someStringValue )
    {
        someString = someStringValue;
    }
}

我如何在MyClass之外获取someString的值?

更新:

抱歉,由于实际的生产代码受到保护,因此我无法在此处使用属性。 我是QA / Dev,我需要一种方法来使这些人私有以编写用户验收测试。 因此,我无法更改生产代码。 你能帮我吗?

这是一个工作的泛型版本,我可以得到它。

private static object GetInstanceField<T>(T instance, string fieldName)
{                
    BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
    FieldInfo field = typeof(T).GetField(fieldName, bindFlags);
    return field.GetValue(instance);
}

用法

var str = (string)GetInstanceField(instance, "someString");

如果它是您的类并且您想在类之外提供对它的访问,请通过公共属性公开它:

public string SomeString { get { return someString; } }

否则,不要在课堂之外使用它。 你不是故意的。

编辑

根据您的评论,您实际上是在对生产代码进行 QA 测试,我会质疑在测试期间需要访问私有值的原因。 你真的应该只需要测试公开暴露的属性/方法来验证一切是否按预期运行。

话虽如此,如果真的没有办法解决问题,您可以使用反射来获取值。

如果您将它用于单元测试,我实现了 @dcp 答案的通用版本,它提供了以下附加功能:

  • 断言该字段是否存在
  • 断言值是否为“T”类型。
  • 将值转换为“T”类型。

代码:

private const BindingFlags BindFlags = BindingFlags.Instance 
                                       | BindingFlags.Public 
                                       | BindingFlags.NonPublic
                                       | BindingFlags.Static;

/// <summary>
/// Uses reflection to get the field value from an object.
/// </summary>
/// <param name="type">The instance type.</param>
/// <param name="instance">The instance object.</param>
/// <param name="fieldName">The field's name which is to be fetched.</param>
/// <returns>An instance of <see cref="T"/>.</returns>
internal static T GetInstanceField<T>(Type type, object instance, string fieldName)
{
    var field = type.GetField(fieldName, BindFlags);
    Assert.IsNotNull(field, string.Format("The field with name '{0}' does not exist in type '{1}'.", fieldName, type));
    var value = field.GetValue(instance);
    Assert.IsInstanceOfType(value, typeof(T), string.Format("The value of the field '{0}' is not of type '{1}'", fieldName, typeof(T)));
    return (T)value;
}

您可以使用此扩展方法。

public static class Extensions
{
    public static object GetFieldValue(this object instance, string fieldName)
    {
        const BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
        var field = instance.GetType().GetField(fieldName, bindFlags);
        return field == null ? null : field.GetValue(instance);
    }
}

除了@dcp 答案之外,将其转换为通用函数会更容易...

internal static T GetInstanceField<T>(object instance, string fieldName)
{
BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic
    | BindingFlags.Static;
FieldInfo field = type.GetField(fieldName, bindFlags);
return field.GetValue(instance);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM