34

Here is the structure:

MyClass : SuperClass2

SuperClass2 : SuperClass1

superClass2 is in Product.Web and SuperClass1 is in the .NET System.Web assembly

I'm trying to force a value into a private bool field on SuperClass1. But not matter what I try I cannot get the fields to come back from reflection.

I'm using the following code with different BindingFlag combinations but nothing has worked so far. SuperClass1 is an abstract class.

((SuperClass1)this).GetType().GetFields(System.Reflection.BindingFlags.NonPublic);

Notes: When I use GetProperties() I get back a nice big list but when I specify any bindingflags I get nothing even though there are matching properties. Whats the deal?

Also, the field is not marked internal

Obvisouly I would be using GetField(string name, BindingFlags) but I can't even get GetFlags() to work.

Update: I've tried adding BindingFlags.Instance as suggested but it doesn't work (as expected anyway). I get back 2 fields that are coming from the class SuperClass1 inherits from. Returns null when used with GetField(string name, Flags)

Here is the code for the base class I'm trying to get the field for

public abstract class BaseValidator : Label, IValidator
  {
    private bool propertiesChecked;
...
}
Dustin Davis
  • 14,482
  • 13
  • 63
  • 119
  • Sorry for a stupid question but why are you doing this? :) – sll Aug 05 '11 at 19:47
  • 2
    Great question. Because of Product.Web.SuperClass2, I have to do this as a work around to implement support for a feature that they should already support but dont. Basically the API has a virtual method but ALWAYS calls the base implementation. So I need to change the value of the field so that it doesnt do certain checks so I can get my code to run. – Dustin Davis Aug 05 '11 at 19:50
  • 8
    Please don't ask "why do you want to do this?" -- it's not helpful or constructive -- there are a million valid reasons for doing this, and all are different. Obviously the code smells, but dogma isn't going to help the 900 thousand of us that will be dumped here via google. – BrainSlugs83 Jul 18 '17 at 19:47
  • In my case I have engine code that is probably broken, but I can't prove it unless I can introspect at runtime, and my toolkits do not permit standard debugging. So I am grasping at straws. PS. I would never deploy code that does this for production. – Fred Haslam May 29 '23 at 22:16

5 Answers5

50

You can manually go up in the inheritance chain to get the base fields:

Given these classes:

class SuperClass1
{
    private int myField;
}

class SuperClass2 : SuperClass1
{
}

class MyClass : SuperClass2
{

}

This should work:

var myObj = new MyClass();
var myField = typeof(MyClass).BaseType
                             .BaseType
                             .GetField("myField", BindingFlags.Instance | BindingFlags.NonPublic);

There's a more generic solution in this SO answer: Not getting fields from GetType().GetFields with BindingFlag.Default

Community
  • 1
  • 1
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
13

In a similar vein to BrokenGlass's solution, you could do this to make it a bit more generic:

class Base { private int _baseField; }
class Derived : Base { }
class Mine : Derived { }

And then:

Type t = typeof(Mine);
FieldInfo fi = null;

while (t != null) 
{
    fi = t.GetField("_baseField", BindingFlags.Instance | BindingFlags.NonPublic);

    if (fi != null) break;

    t = t.BaseType; 
}

if (fi == null)
{
    throw new Exception("Field '_baseField' not found in type hierarchy.");
}

As a utility method:

public static void SetField(object target, string fieldName, object value)
{
    if (target == null)
    {
        throw new ArgumentNullException("target", "The assignment target cannot be null.");
    }

    if (string.IsNullOrEmpty(fieldName))
    {
        throw new ArgumentException("fieldName", "The field name cannot be null or empty.");
    }

    Type t = target.GetType();
    FieldInfo fi = null;

    while (t != null)
    {
        fi = t.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);

        if (fi != null) break;

        t = t.BaseType; 
    }

    if (fi == null)
    {
        throw new Exception(string.Format("Field '{0}' not found in type hierarchy.", fieldName));
    }

    fi.SetValue(target, value);
}

And then:

Mine m = new Mine();

SetField(m, "_baseField", 10);
Shibumi
  • 1,369
  • 9
  • 24
10

Extension method:

/// <summary>
/// Returns the FieldInfo matching 'name' from either type 't' itself or its most-derived 
/// base type (unlike 'System.Type.GetField'). Returns null if no match is found.
/// </summary>
public static FieldInfo GetPrivateField(this Type t, String name)
{
    const BindingFlags bf = BindingFlags.Instance | 
                            BindingFlags.NonPublic | 
                            BindingFlags.DeclaredOnly;

    FieldInfo fi;
    while ((fi = t.GetField(name, bf)) == null && (t = t.BaseType) != null)
        ;
    return fi;
}
Glenn Slayden
  • 17,543
  • 3
  • 114
  • 108
3

I think you need to add the System.Reflection.BindingFlags.Instance flag. Use | to combine it with the NonPublic flag.

EDIT:

Looks like BrokenGlass has it right. I wrote the following quick test.

var fields = test.GetType().BaseType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (var field in fields)
{
     System.Console.WriteLine(field.Name);
}

It correctly reports the field you were looking for. (Test is derived from BaseValidator)

Nathanael
  • 1,782
  • 17
  • 25
  • There's a permission you need to have in order to reflect private fields. Could you provide the field you're trying to access and the class you're deriving from in System.Web? (You will definitely need the instance flag for this to work though.) – Nathanael Aug 05 '11 at 19:52
  • " System.Web.UI.WebControls.BaseValidator" and I want to set propertiesChecked – Dustin Davis Aug 05 '11 at 19:57
0

If hierarchy is static, the simplest way to do this:

var field = typeof(SuperClass1).GetField("_privateBaseField",System.Reflection.BindingFlags.NonPublic);
gdbdable
  • 4,445
  • 3
  • 30
  • 46