简体   繁体   中英

C# Checking and calling object properties that may, or may not exist

Building a proof of concept, I've gotten most of it solved, this is just the final step of my proof of concept and I'm hitting a brick wall. I'm building an automatic constructor for a left nav on a ASP.net with Razor Pages based upon the specific definitions contained within a page. The idea is a universal navbar at the top, and a page specific navbar on the left.

    List<object> navElements = new List<object> {
        new {text = "Test1", location = "test1", subElements = new List<object> {
            new {text = "Test1-1", location = "test1-1", subElements = new List<object> {
                new {text = "Test1-1-1", location = "test1-1-1"},
                new {text = "Test1-1-2", location = "test1-1-2"},
                new {text = "Test1-1-3", location = "test1-1-3"}
            } },
            new {text = "Test1-2", location = "test1-2", subElements = new List<object> {
                new {text = "Test1-2-1", location = "test1-2-1"},
                new {text = "Test1-2-2", location = "test1-2-2"},
                new {text = "Test1-2-3", location = "test1-2-3"}
            } },
            new {text = "Test1-3", location = "test1-3", subElements = new List<object> {
                new {text = "Test1-3-1", location = "test1-3-1"},
                new {text = "Test1-3-2", location = "test1-3-2"},
                new {text = "Test1-3-3", location = "test1-3-3"}
            } }

        } }
    };

Notice this test definition is attempting to delve three levels in. I've built a recursive function to catch every layer of subElements contained within the list. The problem arises that because subElement doesn't exist on the final layer, it throws a 'object' does not contain a definition for 'subElements' and no accessible extension method 'subElements' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)

It can find both universal properties (text and location).

Found this Stackflow question that suggested to use OBJECT.GetType().GetProperty("PROPERTYNAME") != null , and that seems to be working just fine, however I still am unable to compile when calling that property after this if block.

How am I able to check for, and call, a variably existing property inside of a generic object?

This check needs to catch for two actions:

  1. Assigns necessary stylesheet code to allow for the drop box in each layer. Since the existence of the drop box is dependent on the existence of a sub layer.
  2. To trigger the recursion of lower layers. This would enable infinite layers of drop boxes since it's functionized.

Here's the general appearance of the functionized builder.

@{
    void navLeftBuilder(List<object> elements)
    {
        foreach (object element in elements)
        {
            @if (element.subElements) { navLeftBuilder(element.subElement); }
        }
    }
}

I've even tried changing the object call to

@{
    void navLeftBuilder(List<object> elements)
    {
        object obj;
        foreach (int element in elements)
        {
            obj = elements[element];
            @if (obj.subElements) { navLeftBuilder(obj.subElement); }
        }
    }
}

Try with something like the following

class NavElement { 
        public string Text { get; set; }
        public string Location { get; set; }

        public List<NavElement> SubElements { get; set; }
    }

    private static void Main()
    {
        List<NavElement> navElements = new List<NavElement> {
        new NavElement{Text = "1", Location = "1", SubElements = new List<NavElement> {
            new NavElement{Text = "1-1", Location = "1-1", SubElements = new List<NavElement> {
                new NavElement{Text = "1-1-1", Location = "1-1-1"},
                new NavElement {Text = "1-1-2", Location = "1-1-2"},
                new NavElement {Text = "1-1-3", Location = "1-1-3"}
            } },
            new NavElement {Text = "1-2", Location = "1-2", SubElements = new List<NavElement> {
                new NavElement {Text = "1-2-1", Location = "1-2-1"},
                new NavElement {Text = "1-2-2", Location = "1-2-2"},
                new NavElement {Text = "1-2-3", Location = "1-2-3"}
            } },
            new NavElement {Text = "1-3", Location = "1-3", SubElements = new List<NavElement> {
                new NavElement {Text = "1-3-1", Location = "1-3-1"},
                new NavElement {Text = "1-3-2", Location = "1-3-2"},
                new NavElement {Text = "1-3-3", Location = "1-3-3"}
            } }
        } }
        };

With this schema, you can access the properties and the children without reflection.

Console.WriteLine(navElements[0].SubElements[2].SubElements[2].Text); 

will print

1-3-3

EDIT

For the record, this is what would work, but what you should NOT do.

void navLeftBuilder(List<dynamic> elements)
{
    foreach (dynamic element in elements)
    {
        Console.WriteLine($"I'm '{element.text}'");
        try
        {
            navLeftBuilder(element.subElements);
        }
        catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException)
        {
            //  property doesn't exist, ignore
        }
    }
}

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