0

I really had no idea how to title this question but try and understand my question :)

I am trying to learn C#, creating a sample project. I want to know how I can iterate trough a collection which contains interfaces, these interfaces have children with their own interfaces and so on and so forth. The depth is unknow and different for every client I would connect to. I know how I would do it in nested foreach loops, but as I just mentioned, the depth is uknown and I know that the way my brain works right now - using for loops - is just dumb and there are better way of doing this without losing my mind wiring infinite foreach loops. The answer to this question will probably also answer my next question, which I might not post, since I feel like they are so related.

Here is my method that I could create N for loops, but I know there is a better way - probably reflection - I am only starting to read on it, so if anyone could give me an example of how to get these types, maybe in a while loop where I would just change the string of the property I am trying to find? I want to collect ALL ISymmbol.InstancePath properties in the collection of ISymbolCollection<ISymbol> and store them in the list of strings.

    public static List<string> SymbolsCollection(AdsClient adsClient)
    {
        // List of symbol names as string that the method will return 
        List<string> symbols = new List<string>();

        // The collection I receive from my client
        ISymbolCollection<ISymbol> symbolCollection = GetSymbols(adsClient); 

        // Now loping the data inside the received collection
        foreach(ISymbol symbol in symbolCollection)
        {
            // Example of how I store the first, topmost symbol to the list
            symbols.Add(symbol.InstancePath);

            // The symbol contains subsymbols - I need to store it in the list as well
            foreach(ISymbol subsymbol in symbol.SubSymbols)
            {
                symbols.Add(subsymbol.InstancePath);

                //Third time iterating
                foreach(ISymbol subSymbolsSubsymbol in subsymbol.SubSymbols)
                {
                    symbols.Add(subSymbolsSubsymbol.InstancePath);

                    // This could go really deep as you can see
                }
            }
            
        }
        return symbols;
    }

Edit: The Isymbol interface:

    public interface ISymbol : IAttributedInstance, IInstance, IBitSize
    {
        //
        // Summary:
        //     Gets the Symbol/Datatype Category
        //
        // Value:
        //     The category.
        DataTypeCategory Category { get; }

        //
        // Summary:
        //     Gets the parent Symbol
        //
        // Value:
        //     The parent.
        ISymbol? Parent { get; }

        //
        // Summary:
        //     Gets the SubSymbols of the TwinCAT.TypeSystem.ISymbol
        //
        // Remarks:
        //     Used for Array, Struct, Pointer and Reference instances. Otherwise empty
        ISymbolCollection<ISymbol> SubSymbols { get; }

        //
        // Summary:
        //     Gets a value indicating whether this Symbol is acontainer type.
        //
        // Value:
        //     true if this instance is container type; otherwise, false.
        bool IsContainerType { get; }

        //
        // Summary:
        //     Gets a value indicating whether this instance is a primitive type.
        //
        // Value:
        //     true if this instance is primitive type; otherwise, false.
        bool IsPrimitiveType { get; }

        //
        // Summary:
        //     Gets a value indicating whether this TwinCAT.TypeSystem.ISymbol is persistent.
        //
        // Value:
        //     true if this instance is persistent; otherwise, false.
        bool IsPersistent { get; }

        //
        // Summary:
        //     Indicates that this instance is read only.
        //
        // Remarks:
        //     Actually, this Flag is restricted to TcCOM-Objects readonly Parameters. Within
        //     the PLC this is used for the ApplicationName and ProjectName of PLC instances.
        //     Write-Access on these Modules will create an TwinCAT.Ads.AdsErrorCode.DeviceAccessDenied
        //     error.
        bool IsReadOnly { get; }

        //
        // Summary:
        //     Gets a value indicating whether this instance is recursive.
        //
        // Value:
        //     true if this instance is recursive; otherwise, false.
        bool IsRecursive { get; }
    }
}

Please let me know if my question is unclear or you need more information about the data types etc.

I am using .NET 6 framework. I could swap to 4.8 so I think that shouldn't matter in that case, so any solution for either framework will apply (all these frameworks are also confusing, as there are so many - I just went with the newest one when installing VS).

ziga
  • 159
  • 1
  • 6
  • 1
    If you have deeply nested data, you don't really have too many options. You could potentially change your child collections to HashSet which would help with the looping in your code, although there would still be some internal looping. Or you can reevaluate your data structure entirely and look at something tree-based. – David L Oct 13 '22 at 18:56
  • Please [edit] your question to include the source code of the `ISymbol` interface. Can you use a recursive approach? – Progman Oct 13 '22 at 18:57
  • Added the ISymbol interface. – ziga Oct 13 '22 at 19:04
  • @DavidL Can you explain what you mean with the tree-based? The data structure is something I receive from a 3rd party and is not my source code unfortunately. – ziga Oct 13 '22 at 19:10
  • 1
    Or maybe you are trying to solve one thing and directing readers to think over your code which may be unrelated? (XY problem). IOW, why don't you instead give some samples and what you are trying to do. – Cetin Basoz Oct 13 '22 at 19:10
  • This is the sample. I want to get all the smybol paths and then display them in a treeview. To do that I want to store the paths in the list of strings. But I am not sure if there is a better way of doing this, as I wrote, I am not that experience with C#, just getting into it... – ziga Oct 13 '22 at 19:20
  • Going _endless_ deep can be achieved by recursion. You need a meaningful finish state and depending on the depth you could run into an stack overflow exception. In that case you would need something more advanced like tail recursion, but first start with normal recursion. – Oliver Oct 13 '22 at 19:27
  • Use recursivity? – paradise Oct 13 '22 at 19:44
  • @Progman that thread did the trick! The second answer with the extension class method SelectManyRecursive did it! Thanks a lot :) I am assuming this is still time consuming (CPU time) since it literally flattens the entire source in to a single sequence? Haven't done much with linq but seems very useful for my particular problem. – ziga Oct 13 '22 at 20:16

0 Answers0