How to get the value of private field using reflection?
Asked Answered
F

7

70

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 change production code. Can you help?

Flemish answered 21/7, 2010 at 19:39 Comment(0)
S
110

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.

Suprematism answered 21/7, 2010 at 19:45 Comment(8)
Great! That's all I need. My issue here is that I can't change production code. Thanks a lot dcp ;)Flemish
If it solved the problem, please don't forget to accept the answer. Glad to help, however, I tend to agree with the others here that maybe this isn't the best idea in your situation for the great reasons already mentioned. Good luck!Suprematism
Hi pcd, I got compiler error that said I can't cast instance to MyClass object? Can you show me how to assign the value that we got into a string named "str"? Thanks,Flemish
Unit testing is the one time where it is NEVER acceptable. If you can't unit test something using just its public API then you have a serious design flaw that needs to be addressed.Vestment
@user398398 - See my latest edit, you just need to cast the return value, which can be done using the "as" operator or direct cast.Suprematism
@Johnathan Allen - Good point, but it depends on what your goal is. Where this has come up for me is when I need to get code coverage for some catch block and the only way to do it is by setting some private variable to null so code will fail within the method. I agree with you in theory, but in actual practice where code coverage percentage is a measured item, it becomes necessary at times. The only time I do it is in actual competitions where higher code coverage can give you a better score, and lower code coverage can result in a deduction.Suprematism
Thanks for the answer; I needed those binding flags!Lime
I wouldn't say "only in test" - Microsoft puts out some horribly deficient common controls, the PropertyGrid for instance can only be made useable in a wide range of scenarios by peeking at private properties.Fecundate
R
16

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.

Reprehension answered 21/7, 2010 at 19:40 Comment(5)
Amen. If you can't produce a desired situation using regular code, that's probably an indication that it was programmed wrong. If you can't produce an undesired situation using regular code, that's all the better: no need to test that situation. If there are exceptions to this, they are very rare.Cake
There are situations where you have to work with private fields to work around bugs in other peoples code. It isn't pleasant, sometimes there is no alternative.Vestment
@Jonathan: I can't remember when I've had to do so in production code, and when it's test code for production code in the same company, we're talking about a different kind of situation.Reprehension
I've had to only once. I really hated doing it, but I needed to override ObservableCollection.CollectionChanged. You can do this directly in C#, but in VB you have to fake it by accessing the private field directly.Vestment
Just yesterday I submitted a PR that fixes a bug in .NET 8 and until the PR is merged - I reflect the heck out of it to fix the internals like I need them to.Divulge
G
5

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");
Gileadite answered 19/10, 2018 at 9:32 Comment(0)
N
1

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;
}
Niemann answered 8/12, 2015 at 14:4 Comment(0)
A
0

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.

Alleviation answered 21/7, 2010 at 19:40 Comment(8)
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 change production code. Can you help?Flemish
Why are you checking private data for User Acceptance Testing? You should only really need to Test the public properties/methods of the class since those are what are going to be consumed.Alleviation
Because our customer just asked for that advance feature. The thing here is that we need to write the test first to show them that it won't be broken.Flemish
Now I'm really confused. Your customer just asked you to provide a way to get the value of private values even though they've been explicitly marked as private by the developers? If that's the case, then this should really go back to Development and they should expose things properly through Properties.Alleviation
Indeed - how does the customer even know about the private field? What are they really asking you to test?Reprehension
Hi Justin, thanks a lot for your idea. I understand what you said, but I'm just an intern here. I can't do anything to get around with it.Flemish
@user398398: Raise it with your manager. Part of being an intern is meant to be learning the right way to do things - and you're being asked to do something which just isn't right. I know if any of the interns we have in the office started complaining about this sort of thing, they'd be cheered on by the other engineers :)Reprehension
@user398398 - I'm with Jon on this one. We'd treat the situation the same way here.Alleviation
L
0

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);
    }
}
Liquidize answered 21/4, 2015 at 9:6 Comment(0)
B
0

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);
}
Bleed answered 23/11, 2016 at 12:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.