如何在C#中获取私有字段的值?

时间:2010-07-21 19:39:25

标签: c#

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

class MyClass 
{
    private string someString;

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

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

更新

抱歉,我不能在这里使用属性,因为实际的生产代码受到保护。我是一名质量保证/开发人员,我需要一种方法让那些私人用于编写用户验收测试。所以我无法改变生产代码。你能帮我吗?

9 个答案:

答案 0 :(得分:76)

正如其他人所说,由于该字段是私有的,因此您不应该尝试使用普通代码来获取它。 唯一可以接受的是在单元测试期间,即使这样你也需要一个很好的理由(例如将私有变量设置为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;

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

答案 1 :(得分:16)

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

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

public string SomeString { get { return someString; } }

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

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

答案 2 :(得分:5)

使用反射,但要准备好用其他程序员的大棒击打。

答案 3 :(得分:2)

如果您是班级,并且想要在班级之外提供访问权限,请通过公共财产公开:

public string SomeString { get { return someString; } }

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

修改

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

所有这一切,如果你真的没办法解决问题,你可以使用Reflection来获取价值。

答案 4 :(得分:2)

这是一个可以正常使用的通用版本。

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");

答案 5 :(得分:1)

如果您正在使用它进行单元测试,我实现了@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;
}

答案 6 :(得分:0)

您可以使用此扩展方法。

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

答案 7 :(得分:0)

除了@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);
}

答案 8 :(得分:-3)

将其设为公共财产

public string SomeString {get; private set;}

该属性声明将允许任何外部类读取变量但不修改它(因此私有“setter”)。

编辑:刚看到你对另一个答案的评论。您将不得不使用Relection来获取类似的私有变量。正如在另一个建议的答案中,一定要注意程序员用棍棒;)。也就是说,谷歌的一些事情就是“用反射C#寻找物业价值”。