开发者

How to get the value of private field using reflection?

I ran into a problem that I need to access to private field of a class. For example:

class MyClass 
{
    private string someString;

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

How can I get the value of someString outside MyClass ?

Update:

Sorry, I cannot use property here since the the actual production code is protected. I'm a QA/Dev, I need a way to get those private for writing User Acceptance Test. So I cannot cha开发者_StackOverflownge production code. Can you help?


As others have said, since the field is private you should not be trying to get it with normal code. The only time this is acceptable is during unit testing, and even then you need a good reason to do it (such as setting a private variable to null so that code in an exception block will be hit and can be tested).

You could use something like the method below to get the field:

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

So you could call this like:

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

Again, this should not be used in most cases.


You can't - and you're not meant to. It's private. If this is someone else's class, then clearly they don't want you to have access to that field. The fact that it's private allows them to change the implementation later - they might end up with that value as part of another variable, or renamed, or possibly gone completely if it's no longer required in order to implement the public API.

If it's your own class and you're sure you want other people to be able to access it, just expose it with a property:

public string SomeString { get { return someString; } }

EDIT: Having seen your comments, you can access the private fields with reflection... but for an acceptance test you shouldn't have to. You should be testing the public API. For unit tests it makes sense to bend the rules sometimes, and treat the class as a "white box" rather than doing "black box" testing, but for acceptance tests I would definitely stick to the public API.

If this doesn't help, I suggest you talk to the developers of the production code: explain why you want access, and ask them to expose it via a property. They could make it an internal property, and use [InternalsVisibleTo] to get access to it in your test assembly. I would personally prefer this over using reflection - otherwise if the production code changes in a perfectly valid way, your tests will fail when they shouldn't.


Here is a working generics version as clean as I can get it.

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

usage

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


If you are using it for unit testing, I implemented a generic version of @dcp 's answer which provides the following extras:

  • Asserts if the field exists
  • Asserts if the value is of type "T".
  • Converts the value to the type "T".

Code:

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


If it's your class and you want to provide access to it outside of the class, expose it via a public property:

public string SomeString { get { return someString; } }

Otherwise, don't use it outside of the class. You're not meant to.

EDIT

Based on your comment that you're actually doing QA testing on production code, I'd question the reasoning behind needing to access a private value during your testing. You really should only have to test the publicly exposed Properties/Methods to verify that everything is functioning as intended.

All that being said, if there's really no way around things you can use Reflection to get the values.


You can use this Extension method.

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


in addition to @dcp answer, this can be a lot easier by turning it into Generic Function...

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜