1

I have a complex class hierarchy: class B is an attribute of class A, List(class C) and class D are attributes of class B, etc - lots of levels of parent-child relationship. Some classes in the hierarchy has string attribute "foobar". Some classes don't. I have an instanse of class A. I need to find all objects in the hierarchy which has attribute "foobar", and change its value to "qwerty". Is there a simple way to do that in C#?

public class ClassD
{
    public string fooBar;
}
public class ClassC 
{ }
public class ClassB
{
    public List<ClassC> classCList;
    public ClassD classDInstance;
    public string fooBar;
}
public class ClassA
{
    public ClassB classBInstance;
}
Pavel Sinkevich
  • 619
  • 5
  • 12
  • Do you mean attributes in the sense of `[XyzAttribute]` or something else? – stuartd Feb 18 '16 at 15:56
  • Something like that: `public class ClassD { public string FooBar;} public class ClassC { } public class ClassB { public List ClassCList; public ClassD classDInstance; public string FooBar; } public class ClassA { public ClassB ClassBInstance; }` – Pavel Sinkevich Feb 18 '16 at 16:01
  • Can you add edit that into the question? It's not readable as a comment. – stuartd Feb 18 '16 at 16:08
  • You say you want to 'do this in C#', so are you looking for code which will rename the fields for you, rather than using eg Resharper or Visual Studio refactoring? – stuartd Feb 18 '16 at 16:33

2 Answers2

3

You can use Reflection, although I'm not a big fan of such, I'd guess that is the easiest way to acomplish what you need.

The code below is not very pretty, but hopefully will give you an idea.

    private static void replaceFoobar(object obj)
    {
        if (obj == null)
            return;

        const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Instance;
        FieldInfo[] members = obj.GetType().GetFields(bindingFlags);

        if (!members.Any())
            return;

        foreach (var item in members)
        {
            if (item.Name == "fooBar" || item.Name == "<fooBar>k__BackingField")
            {
                item.SetValue(obj, "qwerty");
            }
            else
            {
                object value = item.GetValue(obj);

                if (value != null && value is IEnumerable)
                {
                    foreach (var itemE in ((IEnumerable)value))
                        replaceFoobar(itemE);
                }
                else
                    replaceFoobar(value);
            }
        }
    }
wbsantos
  • 79
  • 3
2

It looks like you want to change the value of an attribute with a given name.

This is pretty ugly but might give you some idea how to accomplish what you want using reflection:

static void Set<T>(dynamic obj, string property, T value) {
    //Iterate through the fields on your object.  Can change this to properties or do both.
    foreach (var field in obj.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)) {
        var fieldValue = field.GetValue(obj);

        //If we have a collection, then iterate through its elements.
        if (field.FieldType.GetInterface("System.Collections.IEnumerable") != null && !(fieldValue is string) && fieldValue != null)
            foreach (var item in fieldValue) SetField<T>(item, property, value);
        //If field name and type matches, then set.  
        else if (field.Name == property && field.FieldType == typeof(T)) field.SetValue(obj, value);
    }
}

You'd call it as follows:

Set(obj, "fooBar", "qwerty");

Note that this only iterates through fields at the moment because that is how your classes are set up (public fields, not properties). If you want to include properties, you can change it to work with properties, or combine fields and properties and iterate through both.

Again, I'm not saying to use this approach, but might give you some ideas.

Community
  • 1
  • 1
lintmouse
  • 5,079
  • 8
  • 38
  • 54