0

I am trying to iterate through Classes who have properties not linked to other Classes (i.e. public SomeOtherClass code { get; set; }

In my attempt and thanks to other people's advice, I came up with the following, which iterates through the classes of a specific namespace (i.e. Library = "ConsoleApp1.Library" etc) as:

        var classes = AppDomain.CurrentDomain.GetAssemblies ()
                                .SelectMany (t => t.GetTypes ())
                                .Where (t =>
                                            t.IsClass &&
                                            t.Namespace == Library &&
                                            !t.IsNested)
                                .ToList ();

and getting properties as

        var properties = classes.SelectMany (x => x.GetProperties (BindingFlags.Public | BindingFlags.Instance)
                                .Select (y => y.PropertyType));

while I get what I need from a foreach loop:

        foreach ( var method in classes.Where 
                (x => !properties.Contains(x) && !properties.Contains (x))
                .Select (x => x.Name) )
        {
        // some work here
        }

However, some cases slipped through my selection; cases where the class has the below Properties as Array, ICollection, List and Dictionary:

public class TopObject
{
    [JsonProperty("ex")]
    public OtherResults ex { get; set; }

    [JsonProperty ("Results")]
    public OtherResults Results { get; set; }

    private readonly OtherResults[,] Codes1 = new OtherResults[9, 9];
    public ICollection<OtherResults> Codes2 { get; set; }
    public List<OtherResults> Codes3 { get; set; }
    public Dictionary<int, OtherResults> Map { get { return _map; } }

    public TopObject()
    { Results = new OtherResults (); }
}

public class OtherResults
{
    [JsonProperty ("Jcodes")]
    public string Jcodes { get; set; }

    public OtherResults()
    { }
}

I need help in editing the var method, to include (additionally) the cases where a property has Dictionary Value type any of the classes, or is Array of type of any of the classes, or is ICollection or List who accept any knows classes.

Kindly someone can help me with this thing? Thank you very much

Trevor
  • 7,777
  • 6
  • 31
  • 50
Nick
  • 483
  • 1
  • 6
  • 15
  • 1
    Specifically any kind of collection of objects or any references in generics in general? And how about a `List>`? I assume that also counts as reference towards `OtherResults`? –  Feb 10 '20 at 14:48
  • This shows you how to get the generic type parameters, either filter on the specific generics you want to support or check them all: https://stackoverflow.com/questions/1043755/c-sharp-generic-list-t-how-to-get-the-type-of-t –  Feb 10 '20 at 14:51
  • @Knoop Thank you very much for looking this up; you are absolutely correct, yes counts as reference and I want to exclude it from my results. – Nick Feb 10 '20 at 14:58
  • @Knoop: Thank you for the link, extremely interesting; I just don't know how to proceed from `type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)` to actually examine all those types (array, dictionary etc..) with respect to types of my classes.. But I see your point. – Nick Feb 10 '20 at 15:02
  • I have a feeling you don't care about what kind of generic it is, you just want to dive into the generic type parameters. If a class had a property that was generic but had nothing to do with a list I assume you still want to exclude any classes referenced in that generic definition. So I would skip the entire `type.GetGenericTypeDefinition() == typeof(List<>)` and just extract the types returned by `GetGenericArguments`. Keep in mind though that those types can also be generics themselves so you would have to do this recursively. –  Feb 10 '20 at 15:11
  • @Knoop All I am trying to do is get classes who they are not referenced in any other class. So if a class has a property `public Dictionary Map { get { return _map; } }` that's ok, the moment is "touched" by any of the other classes'properties, the whole referenced class should be excluded. Same for a ClassX that has a property array of type X, and *another* ClassY within the namespace has a property that is of type ClassX, then ClassX should be excluded i.e. `Public readonly UnluckyClass[,] someProp = new OtherResults[....` (class UnluckyClass should be excluded). – Nick Feb 10 '20 at 16:05
  • I tried to load a working example in fiddle, but seems not everything is supported. I believe if you loaded into VS will work (compiles at my side). Any observation, I will try to change/update/debug. This is the link: https://dotnetfiddle.net/TlWSM1 – Nick Feb 10 '20 at 17:23
  • @Knoop Dotnetfiddle updated.. – Nick Feb 10 '20 at 18:11

1 Answers1

1

So I finally had time to play around with this a bit. If I understand correctly you would like something like this:

First we create a helper function that extracts all referenced types:

public static IEnumerable<Type> GetReferencedTypes(Type type)
{
    if (type.IsArray)
        return new List<Type> { type, type.GetElementType() };
    if (type.IsGenericType)
        return type.GetGenericArguments().SelectMany(GetReferencedTypes)
            .Union(new List<Type>{type});
    return new List<Type>{ type };
}

I can't guarantee that this covers everything but atleast it seems to get all the references from your examples. Array is special here, it's not a generic but we have to extract it's underlying type. All generic types we extract recursively. and if it's not any of those we just return the type that was given.

So with this helper function we can now do something like this:

var currentAssembly = typeof(Program).Assembly;

var currentAssemblyName = typeof(Program).Assembly.GetName().Name;

var types = currentAssembly.GetTypes();

var classes = types.Where(type => type.IsClass && !type.IsNested).ToList();

var referencedTypes = classes.SelectMany(c => c.GetProperties().SelectMany(p => GetReferencedTypes(p.PropertyType))).Where(type => type.Assembly.GetName().Name == currentAssemblyName).Select(type => type.Name)
    .Distinct().ToList();

We get the current assembly. Get all types, then put all properties of those types through our helper function, select their name, remove duplicates and create a list. Which results in:

["Method100Response201_01", "Method600Response100", "Method700Response100", "Method100Response404_01"]

Keep in mind that in your fiddle change the public Method500Response100[,] Codes1 = new Method500Response100[9, 9]; is not returned by the GetProperties() call (it has no { get; set; }).

Hope this helps

  • Apologies for delayed response, a big THANK YOU for looking such a complicated matter, I am testing it asap. – Nick Feb 11 '20 at 21:29
  • I updated fiddle with proposed solution (good eye!! thank you for spotting the missing getsetters!) and you may have a look. So, thing is, I have in return a class name with an array symbol, like, `Method500Response100[]` and then also again `Method500Response100`. The `Method500Response100` however is correct. But unfortunately throws also two classes that are being reference, in the fiddle is more visible (I didn't changed the model, is same as example, probably the names differ: the set still includes referenced classes that are IList, Dict, ICollection and array.. that's it). – Nick Feb 11 '20 at 22:09
  • I just did a foreach loop, if there's something more I should do, please correct me. I think you are mighty close to a definite solution and better than the one I have, so, thanks again for looking that up. – Nick Feb 11 '20 at 22:11
  • That's not a bug. `public Method500Response100[] Codes1 { get; set; }` this property is of the type `Method500Response100[]` and by your specification it references the `Method500Response100` type which is why both are added. This is done by this line `return new List { type, type.GetElementType() };` if for some reason you don't want the array types and only the types they reference change it to `return new List { type.GetElementType() };`. –  Feb 12 '20 at 08:48
  • Forgot to mention you in my reply @Nick –  Feb 12 '20 at 09:42
  • I deeply appreciate the effort. I lost you a bit, I am looking for the "top" classes, I mean the class that they have inside them properties of type so, if Class A has property `public IList someField` and Class B has a property `public ClassC[] arrayField`, I am searching just for Class A. If there is also a ClassC with any kind of generic properties, then I am searching Class A and Class C as well. In fiddle, class `Method500Response100` is present in class `TopObject` as field `Codes1` so I wasn't expecting `Method500Response100` in results.. – Nick Feb 12 '20 at 23:41
  • So, now seems the results return a mix of two.. If we could set it up just for the top classes that would be marvelous indeed.. There are no other non-generic types I look to **exclude**, just classes that are being referenced as Dictionaries, arrays, ICollections and Lists, that's all.. If the results return the opposite, then I guess is also ok, but now is a mix (for example, `Method500Response100` is in the results, as expected, but also `Method100Response201_01` which shouldn't.. – Nick Feb 12 '20 at 23:49
  • @Nick I do, but in my opinion the original question has long been answered and I have the feeling you're using this as a personal coding service. Try to understand the code (let me know if you have any questions about that) and use that knowledge to adapt the answer to your needs. –  Feb 15 '20 at 07:37
  • Thank you for getting back to me.. Unfortunately I can't get it work (as per the fiddle, doesn't give the correct results) but I am trying of course by myself. Nonetheless thank you indeed ! – Nick Feb 15 '20 at 10:45