1

There is loads of stuff on here about reflection but I can't seem to get my head around my specific problem.

My Classes:

public class Box
{
   public string Name { get; set; }
   public int Size { get; set; }
}

public class Pallet
{
   public Box b1 = new Box();
   public Box b2 = new Box();
}

The Code for Creating the object:

Pallet p = new Pallet();
p.b1.Size = 5;
p.b2.Size = 10;

The code to display the size of a chosen box:

MessageBox.Show(p.b1.Size.ToString());

I would like to select the box at runtime using a string. i.e.

string boxid = "b1";
Object myObj = p. + boxid;          

MessageBox.Show(myObj.Size.ToString());

Obviously this code will not work. What would be the correct way to get the value of the chosen box in this case 5?

1
  • Why do you want to do this by string? Commented Jan 27, 2016 at 9:12

4 Answers 4

4

Since b1 and b2 are fields, you can get them using GetField. On the FieldInfo, call GetValue providing the instance of the pallet to get the specific field for.

Box box = (Box)typeof(Pallet).GetField(boxId).GetValue(pallet);
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for response, when i use this i get the name 'pallet does not exist in the current context' So i assumed you meant. Box box = typeof(Pallet).GetField(boxId).GetValue(p); but with this i get 'Cannot implicitly convert type 'object' to 'ReflectionNestedObjects.Box'. An explicit conversion exists (are you missing a cast?) ReflectionNestedObjects K:\BIG SOLUTIONS\LEARN\ReflectionNestedObjects\ReflectionNestedObjects\Form1.cs 31 Active
Sorry. I missed the conversion.
4

You can go with reflection to do the job as @PatrickHofman has answered already, but I believe that there's a better solution here: use dictionaries.

If you store these boxes in some property typed as Dictionary<string, Box> in your form or wherever you want and you add the so-called boxes when you instantiate them, life can be easier!

public class Form1 : Form 
{
      public Dictionary<string, Box> Boxes { get; } = new Dictionary<string, Box>();

      public Form1()
      {
          Pallet p = new Pallet();
          p.b1.Size = 5;
          p.b1.Size = 10;

          Boxes.Add("b1", p.b1);
      }
}

...and who knows where, you can get boxes by their identifier as follows:

MessageBox.Show(Boxes[boxId].Size.ToString());

Sometimes we tend to think runtime means reflection, and there're better ways to solve a lot of issues without reflection.

Don't get me wrong: I'm not against reflection, I use it when the use case requires it.

2 Comments

Yes indeed. XY problem.
@PatrickHofman But it's XY problem either way. I just wanted to provide an alternative to reflection.
2

The question forces us to make a lot of assumptions. It could do with more details about where are you passing it from and to. What is on the receiving side? Is it another copy of the same app or is it something else entirely (e.g. is the .net framework available at the receiving end?).

If the .net framework IS available at the receiving end then I would suggest using binary or XML serialisation to wrap up your object into a neat parcel before sending it. Then simply deserialise it at the other end. This way you don't even need to think about what your object contains during the serialisation process, the .net framework does it all for you. This method doesn't need you to explicitly use reflection yourself if you can use the same class definition at both ends.

If this sounds like it would work for you I can put together an example. Let me know in the comments.

EDIT: Further info

Here's a method that isn't entirely hideous... Use XmlSerialiser to convert your class into an XML string, then load that into an XmlDocument object. Once it's in there you can use XPath queries to extract specific nodes of the XML and traverse the entire class structure (in the XML) only by using strings.

Here is a great code sample from Microsoft for how to do exactly this... https://code.msdn.microsoft.com/windowsdesktop/Manipulate-XML-in-memory-4b612d6b/view/SourceCode

As you say in your comment below, your real world class will have many properties and fields, this method is likely to be the most robust and flexible way to do what you need. It will probably be nicely version proof to some extent.

7 Comments

Are you sure you're answering this Q&A? :D
Hi thanks for your advice, Perhaps Reflection is overkill. The actual app is locally stored and does not require to pass anything outside it's self. Basicly I currently use reflection to get the boxes that are in a pallet. i.e. b1 and b2 this populates a combobox the user selects a box and the app will return it's size. the idea being is if need to add a new box all i need to do is add public Box b2 = new Box(); to the pallet object set its' values and the user will automatically have the new option.
@Matías Fidemraizer, yes I'm attempting to suggest serialisation as the technology to use to solve this problem. Pending clarification by the OP I will edit this answer if it turns out that this is the warranted solution.
@Wossname is my previous comment clear? is serialisation still a valid option given that I am not sending the object anywhere out side the app ?
@user3755946, yes it clears things up a lot. I'd say reflection is overkill but your requirements are a little unusual - having to make decisions based on a string matching the name of a variable is a fragile way to do things. I'm not saying it wrong though -- perhaps you have no choice. I'd honestly only be tempted to use reflection or serialisation if your classes have many properties. If your classes really are this small then you should just use something like if(boxd == "b1") MessageBox.Show(p.b1.Size.ToString()); else if(boxd == "b2") MessageBox.Show(p.b2.Size.ToString());...
|
1

A way to use XML like a queryable database (a bit like SQL but with XPath syntax)...

To try this, dump this code into a new Console Application in Visual Studio.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication3
{
    [Serializable]
    public class Pallet
    {
        public class Box
        {
            public string Name { get; set; }
            public int Size { get; set; }
        }

        public Box b1 = new Box();
        public Box b2 = new Box();
    }


    class Program
    {
        static void Main(string[] args)
        {
            //create your object and populate it
            Pallet p = new Pallet();
            p.b1.Name = "Fred";
            p.b1.Size = 5;
            p.b2.Name = "Bob";
            p.b2.Size = 10;


            //#### wrap up the object into XML string ####


            XmlSerializer xs = new XmlSerializer(typeof(Pallet), "PalletNS");
            StringBuilder sb = new StringBuilder();
            XmlWriterSettings xws = new XmlWriterSettings();
            xws.Encoding = Encoding.UTF8;
            XmlWriter xw = XmlWriter.Create(sb, xws);

            xs.Serialize(xw, p); //generate the XML string

            //#### pallet is now stored in XML string inside sb

            Console.WriteLine(sb.ToString());

            //#### unwrap the pallet into an Xml structure that you can query easily with strings

            XmlDocument xdoc = new XmlDocument();
            xdoc.PreserveWhitespace = true;
            xdoc.LoadXml(sb.ToString());

            /* The XML serialized class looks like this...

            <?xml version="1.0" encoding="utf-16"?>
                <Pallet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                        xmlns="PalletNS">
                    <b1>
                        <Name>Fred</Name>
                        <Size>5</Size>
                    </b1>
                    <b2>
                        <Name>Bob</Name>
                        <Size>10</Size>
                    </b2>
                </Pallet>
            */



            XmlNamespaceManager nsmgr = new XmlNamespaceManager(xdoc.NameTable);
            nsmgr.AddNamespace("x", "PalletNS");

            //get a reference to the top of the XML tree
            XmlElement root = xdoc.DocumentElement;

            //run an XPathQuery to pull a node that we're interested...
            //in english: "Grab the Name of the first b2 item"
            XmlNode xn = root.SelectSingleNode("./x:b2/x:Name", nsmgr);

            //and print it's name to the console...
            Console.WriteLine("\n\n" + xn.InnerText);

            Console.WriteLine("\n\nPress enter to quit.");
            Console.ReadLine();
        }
    }
}

This would be very powerful if you want your users to be able to run ARBITRARY queries on your data structure. They could write a query in english and your app could translate that into an XPath query. All without reflection!

I guess you've already found your answer but I thought this was fun to put together. Hope it helps someone.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.