简体   繁体   中英

Is there a way of identifying if a given class has any references to another class?

I can work through the hierarchy with reflection, yes, but it is challenging, because the elements are diverse. So:

class A
{
    string Hello;
}

class B
{
    List<A> Hellos;
}

class E
{
    A OtherRandomness;
}
class D:B
{
   E randomthing;
}

class C:D
{
    string OtherThing;
}

I have an object that is of type C, and I want to be able to find and identify that this has, down the line, a list of A object, and then to process them.

The real life example is a more complex hierarchy than this, of course, but the principle is the same. I have tried a recursive loop through the properties, but I was struggling to find ways of dealing with a) not trying to process string and int objects and b) identifying that a property was something like an enumerable of X. Related is the fact that there might be enumerables of classes that inherit from A.

If anyone can suggest an approach that would make this straightforward, that would be appreciated. I need to be able to do this in a range of places, so I have to be able to start with any class type.

So I start with a type of x. In this case x is C, but x could just as easily be Z, with no links to type A.

Also, this needs to pick up whether I have class D in or not. I want to be able to pick up Hellos and randomthing.

I assume that these are public instance properties. If you want something else, you can adapt the BindingFlags .

The idea of my solution is to test whether the type of the property is generic. If yes, it tests whether one of the type arguments is typeof(A) . However, It does not test nested generic types like List<List<A>> . This would require a recursive approach.

var props = typeof(C).GetProperties(BindingFlags.Instance | BindingFlags.Public)
    .Where(p => p.PropertyType.IsGenericType &&
                p.PropertyType.GenericTypeArguments.Any(t => t == typeof(A)));
foreach (var prop in props) {
    Console.WriteLine(prop);
}

prints:

System.Collections.Generic.List`1[MyNamespace.A] Hellos


More specifically testing for enumerations of A using this type hierarchy:

class A
{
    public string Hello { get; set; }
}

class AA : A
{
}

class B
{
    public List<A> Hellos { get; set; }
    public List<AA> AAHellos { get; set; }
}

class D : B
{
    public A randomthing { get; set; }
}

class C : D
{
    public string OtherThing { get; set; }
}

Test:

var c = new C {
    Hellos = new List<A> {
        new A { Hello = "Hello" },
        new A { Hello = "World" }
    },
    AAHellos = new List<AA> {
        new AA { Hello = "Hello" },
        new AA { Hello = "World AA" }
    }
};
var enumerableOfA = typeof(IEnumerable<A>);
var props = c.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
    .Where(p => enumerableOfA.IsAssignableFrom(p.PropertyType));
foreach (var prop in props) {
    Console.WriteLine(prop);
    var list = (IEnumerable<A>)prop.GetValue(c);
    foreach (var item in list) {
        Console.WriteLine(item.Hello);
    }
}

prints:

System.Collections.Generic.List`1[MyNamespace.A] Hellos
Hello
World
System.Collections.Generic.List`1[MyNamespace.AA] AAHellos
Hello AA
World AA

It might be surprising that this returns lists of AA as well. It happens because of the out keyword in the declaration of the interface IEnumerable<out T> making it covariant.

This will, however, not include, eg Dictionary<int,A> , because the dictionary implements IEnumerable<KeyValuePair<int,A>> . My first solution would return such a property, but then it would require more work to get dictionary entries.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM