technical architecture.

Fun with Generics and Lists in C#

by on May.19, 2011, under .net, csharp, Scripting, Unity

So I’ve been doing a lot of List juggling lately and have run into a few tidbits that I wish I had learned a few months ago. I figure that is always a good cue to write them down for future reference and for any others who are digging into this C# world headfirst also. this is all sort of an exansion to try and get a concrete example of mike_mac’s lamda/delegates/callback articles

Say I have a custom class that I’m using to store a structured set of data, this time we’ll play with “ArmDefs”. ArmDef’s just hold a little string and int based data and a list of ArmDef’s as children, and a second list of GameObjects that contains all of the models for this section of arm for use to serialize to disk, but thats more a story for another time.

Overall this is used to build up a hierarchy that holds the “contents” of a runtime modular rig, and handles the Saving/Loading/Reloading.

To work with this I needed to set up a pile of recursive functions to edit the tree at runtime, since I don’t know the depth of the hierarchy, and rebuild the system as needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[System.Serializable]
//Inheriting from ISerializable gives us
//an easy access to Serialization to disk
public class ArmDef : ISerializable {
    public string Name;
    public List<ArmDef> Children = new List<ArmDef>();
 
    public ArmDef(string iFile)
    {
        this.Name= iFile;
        this.Children = new List<ArmDef>();
    }
 
    public void AddChild(ArmDef child)
    {
        Children.Add(child);
//just wrapping the Add so that we can do "extra"
//stuff here if we want, like add links to parents etc
    }
}

not really building these thru scripts out in the wild but just to build something to work with here.

1
2
3
4
5
6
7
8
9
10
public ArmDef config = new ArmDef("Base Level");
 
//add a couple children
config.AddChild(new ArmDef("Level2a"));
config.AddChild(new ArmDef("Level2b"));
config.AddChild(new ArmDef("Level2c"));
 
//add a couple grandchildren
config.Children[1].AddChild(new ArmDef("Level3a"));
config.Children[1].AddChild(new ArmDef("Level3b"));

ok thats all the boring stuff for now! here is a Generic Recursive function that walks down a tree and does ‘Action<>‘ to all children of ‘parent’. The key here is that the Action<> pretty much rocks. !

1
2
3
4
5
6
7
8
9
10
11
public void HandleArmDef(Action<ArmDef> item, ArmDef parent)
{
    if (parent.Children.Count > 0)
    {
        foreach (ArmDeft in parent.Children)
        {
            HandleArmDef(item, ti);
        }
    }
    item(parent);
}

Now the fun begins when you start to chain this stuff together to “act” on all children.
we can define that “Action item” in the above function to be something else using some Lambda or Delegate type stuff. (I’m learning here, those are the reading points for the moment!)

This will go through the hierarchy and remove all children with the Name of “Level3a”.

1
2
string toDelete = "Level3a";
HandleArmDef(item => { item.Children = item.Children.Where(child => child.Name != toDelete).ToList(); }, config);

In another example, i wanted to search the hierarchy and see if there was a certain value anywhere in the hierarchy. easiest way i found was to just quickly build a flattened list and use the Linq “Any” function to see if the string was found anywhere in the hierarchy.

1
2
3
List<string> flatList = new List<string>();
HandleArmDef(item => { flatList.Add(item.Name ); }, parent);
bool isCurrentlyFlat = flatList.Any(x => x.Contains("Flat"));

in another case, i wanted to insert a new child into the hierarchy as a child of a specific object.

1
2
3
4
5
6
7
8
9
10
11
12
13
void AddChildAddon(ArmDef parent, string addAsChildOf, string newChild)
{
    //for each object 'item' down the hierarchy
    //if the item.Name contains our addAsChildOf string
    //we'll add the newChild as a child to the item.
    HandleArmDef(item =>
    {
        if (item.Name.Contains(addAsChildOf))
        {
            item.Children.Add(new ArmDef(newChild));
        }
    }, parent);
}

there’s plenty more possibilities of things to do here if I’ve explained well enough!
More info on Linq by example


1 Comment for this entry

  • PatHightree

    Interesting article Dave, thanks.
    I found out about linq and generics last year and am frequently amazed how concise your code can get with them.
    Haven’t used Action yet though, looks damn handy.

Leave a Reply

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!