代码之家  ›  专栏  ›  技术社区  ›  roxrook

如何获取c中私有字段的值?

c#
  •  41
  • roxrook  · 技术社区  · 15 年前

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

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

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

    更新:

    抱歉,由于实际生产代码受保护,因此无法在此处使用属性。我是一个QA/DEV,我需要一种方法来获取那些用于编写用户验收测试的私有文件。所以我不能更改生产代码。你能帮忙吗?

    9 回复  |  直到 6 年前
        1
  •  75
  •   dcp    15 年前

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

    您可以使用如下方法获取字段:

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

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

        2
  •  16
  •   Jon Skeet    15 年前

    你不能-你不是故意的。它是 私有的 . 如果这是其他人的类,那么很明显他们不希望您访问该字段。它是私有的这一事实允许它们稍后更改实现——它们可能以该值作为另一个变量的一部分结束,或者重命名,或者如果不再需要它来实现公共API,则可能完全消失。

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

    public string SomeString { get { return someString; } }
    

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

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

        3
  •  5
  •   Jerod Houghtelling    15 年前

    使用反射,但要准备好被一个程序员的大棒击中。

        4
  •  2
  •   Justin Niessner    15 年前

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

    public string SomeString { get { return someString; } }
    

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

    编辑

    基于您对您实际上正在对生产代码进行QA测试的评论,我会质疑在测试期间访问私有值背后的原因。实际上,您只需要测试公开的属性/方法,以验证所有内容是否都按预期工作。

    尽管如此,如果真的没有办法解决问题,你可以利用反射来获得价值。

        5
  •  0
  •   Mikael Engver    10 年前

    您可以使用这个扩展方法。

    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);
        }
    }
    
        6
  •  0
  •   André Correia    9 年前

    如果您正在使用它进行单元测试,我实现了@dcp's answer的通用版本,它提供了以下额外功能:

    • 如果字段存在则断言
    • 如果值为“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;
    }
    
        7
  •  0
  •   willnode    8 年前

    除了@dcp answer之外,通过将其转换为通用函数可以更容易地实现…

    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
  •  0
  •   Jay Byford-Rew    6 年前

    这是一个有效的通用版本,尽我所能的清晰。

    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");
    
        9
  •  -2
  •   Mike G    15 年前

    使之成为公共财产

    public string SomeString {get; private set;}
    

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

    编辑:刚刚看到你对另一个答案的评论。您将不得不使用relation来获取这样的私有变量。和另一个建议的答案一样,一定要用棍子小心程序员;)。这就是说,谷歌的做法是“用反射c寻找房产价值”。