我遇到了一个问题,我需要访问一个类的私有字段。例如:
class MyClass
{
private string someString;
public MyClass( string someStringValue )
{
someString = someStringValue;
}
}
如何在MyClass之外获取someString的值?
抱歉,我不能在这里使用属性,因为实际的生产代码受到保护。我是一名质量保证/开发人员,我需要一种方法让那些私人用于编写用户验收测试。所以我无法改变生产代码。你能帮我吗?
答案 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答案的通用版本,它提供了以下附加功能:
代码:
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#寻找物业价值”。