views:

268

answers:

4

The code below produces this output fine:

This is page one.
This is page two.

But how do I change it so that the PageItem objects are instantiated dynamically from the List<string>?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestInstant
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> pageItemsIdCodes = new List<string>() { "PageItem1", "PageItem2" };
            PageItemManager pageItemManager = new PageItemManager(pageItemsIdCodes);
            pageItemManager.DisplayMenu();

            Console.ReadLine();
        }
    }

    class PageItemManager
    {
        private Dictionary<string, BasePageItem> PageItemRecords = new Dictionary<string, BasePageItem>();
        public PageItemManager(List<string> pageItemsIdCodes)
        {
            //manually
            PageItemRecords.Add("PageItem1", new PageItem1(this));
            PageItemRecords.Add("PageItem2", new PageItem2(this));

            //HOW DO i DO THIS DYNAMICALLY:
            //PSEUDO-CODE:
            //foreach (string pageItemIdCode in pageItemsIdCodes)
            //{
            //    Type t = Type.GetType(pageItemIdCode);
            //    //ERROR: Cannot implicitly convert type 'TestInstant.PageItemManager' to 'TestInstant.BasePageItem'
            //    BasePageItem pageItem = Activator.CreateInstance(t, new BasePageItem[] { this });
            //    PageItemRecords.Add(pageItemIdCode, pageItem);
            //}

                    //Type t = Type.GetType(pageItemIdCode);
                    //ERROR: Cannot implicitly convert type 'object' to 'TestInstant.BasePageItem'.
                    //BasePageItem pageItem = Activator.CreateInstance(t); // Change constructor
                    //pageItem.PageItemManager = this; // Add SetMananger call to BasePageItem
                    //PageItemRecords.Add(pageItemIdCode, pageItem);

        }

        public void DisplayMenu()
        {
            foreach (var pageItemRecord in PageItemRecords)
            {
                Console.WriteLine(pageItemRecord.Value.Title);
            }
        }
    }

    class BasePageItem
    {
        public string Title { get; set; }
        protected PageItemManager pageItemManager;
        public BasePageItem(PageItemManager pageItemManager)
        {
            this.pageItemManager = pageItemManager;
        }
    }

    class PageItem1 : BasePageItem
    {
        public PageItem1(PageItemManager pageItemManager)
            : base(pageItemManager)
        {
            Title = "This is page one.";
        }
    }

    class PageItem2 : BasePageItem
    {
        public PageItem2(PageItemManager pageItemManager)
            : base(pageItemManager)
        {
            Title = "This is page two.";

        }
    }
}
+1  A: 

Look at the overloads for Activator.CreateInstance. You can pass in constructors in an object array for one one them.

ck
+7  A: 

I believe you can use the Activator.CreateInstance that takes parameters for input.

foreach (string pageItemIdCode in pageItemsIdCodes)
{
   Type t = Type.GetTypeFromProgID(pageItemIdCode);
   Object pageItem = Activator.CreateInstance(t, new object[]{this});
   PageItemRecords.Add(pageItemIdCode, pageItem);
}

Or as groo says, just cast it:

foreach (string pageItemIdCode in pageItemsIdCodes)
{
   Type t = Type.GetTypeFromProgID(pageItemIdCode);
   BasePageItem pageItem = (BasePageItem)Activator.CreateInstance(t, new object[]{this});
   PageItemRecords.Add(pageItemIdCode, pageItem);
}

You could also do the following if you want the actually type returned:

foreach (string pageItemIdCode in pageItemsIdCodes)
{
   Type t = Type.GetTypeFromProgID(pageItemIdCode);
   BasePageItem pageItem = Activator.CreateInstance(t); // Change constructor
   pageItem.SetMananger(this); // Add SetMananger call to BasePageItem
   PageItemRecords.Add(pageItemIdCode, pageItem);
}

Here is the complete working example:

using System;
using System.Collections.Generic;
using System.Text;

namespace TestInstant
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> pageItemsIdCodes = new List<string>();
            pageItemsIdCodes.Add("PageItem1");
            pageItemsIdCodes.Add("PageItem2");
            PageItemManager pageItemManager = new PageItemManager(pageItemsIdCodes);
            pageItemManager.DisplayMenu();

            Console.ReadLine();
        }
    }

    class PageItemManager
    {
        private Dictionary<string, BasePageItem> PageItemRecords = new Dictionary<string, BasePageItem>();
        public PageItemManager(List<string> pageItemsIdCodes)
        {
            //manually
            //PageItemRecords.Add("PageItem1", new PageItem1(this));
            //PageItemRecords.Add("PageItem2", new PageItem2(this));

            foreach (string pageItemIdCode in pageItemsIdCodes)
            {
                Type t = Type.GetType("TestInstant."+pageItemIdCode);
                BasePageItem pageItem = (BasePageItem)Activator.CreateInstance(t, new Object[] { this });
                PageItemRecords.Add(pageItemIdCode, pageItem);
            }

        }

        public void DisplayMenu()
        {
            foreach (BasePageItem pageItemRecord in PageItemRecords.Values)
            {
                Console.WriteLine(pageItemRecord.Title);
            }
        }
    }

    class BasePageItem
    {
        private string mTitle;
        public string Title { get { return mTitle; } set { mTitle = value; } }
        protected PageItemManager pageItemManager;
        public BasePageItem(PageItemManager pageItemManager)
        {
            this.pageItemManager = pageItemManager;
        }
    }

    class PageItem1 : BasePageItem
    {
        public PageItem1(PageItemManager pageItemManager)
            : base(pageItemManager)
        {
            Title = "This is page one.";
        }
    }

    class PageItem2 : BasePageItem
    {
        public PageItem2(PageItemManager pageItemManager)
            : base(pageItemManager)
        {
            Title = "This is page two.";

        }
    }
}
SwDevMan81
That code gets the error that Add is expecting BasePageItem and we are sending object, I changed it accordingly in my example but still can't get the syntax right.
Edward Tanguay
Cast it to BasePageItem.
Groo
I want to send "this" as a *parameter* of the class being instantiated which *inherits* BasePageItem, since BasePageItem expects PageItemManager in its constructor, not BasePageItem itself.
Edward Tanguay
Right, but you can change the constructors (in BasePageItem, PageItem1, and PageItem2) to be paramterless and add a method in your base class (BasePageItem) called SetManager that would do the assignment that your constructor is doing now.
SwDevMan81
@SwDevMan81 I changed it to a parameter and posted the code above but it still gets a casting error because Object is not a BasePageItem but if I change Object to BasePageItem, then it complains that "t" is not a BasePageItem and I can't seem to cast "t".
Edward Tanguay
I posted the complete working example.
SwDevMan81
That works, it was the adding of the namespace that did it: Type t = Type.GetType("TestInstant." + pageItemIdCode), thanks for your tenacity.
Edward Tanguay
+1  A: 

Might I suggest Assembly.CreateInstance() You can use this function to create an instance of a specified assembly.

Achilles
Generally you'd use `Activator.CreateInstance` instead of `Assembly.CreateInstance`, but the latter is useful if you need to create the type from a specific assembly (for example, if two assemblies have the same type in the same namespace).
280Z28
+3  A: 

This compiles successfully. I have just tried it.

foreach(string pageItemIdCode in pageItemsIdCodes) {
    Type t = Type.GetType(pageItemIdCode);
    BasePageItem pageItem = (BasePageItem) Activator.CreateInstance(t, new object[] { this });
    PageItemRecords.Add(pageItemIdCode, pageItem);
}

It then fails at runtime because the pageItemIdCode values are incorrect. Changing them as follows:

List<string> pageItemsIdCodes = new List<string>() { "TestInstant.PageItem1", "TestInstant.PageItem2" };

makes the code run successfully.

You need to keep in mind that Reflection methods always return null when they cannot find something, rather than raise an exception. If your code expects Type.GetType to always succeed, then you need to check for nulls and raise your own exception, otherwise you will get cascading failures as the nulls are propogated through the rest of the code.

Christian Hayter
Interesting: your solution compiles but gives me a runtime error of "The value may not be null". It seems to want to cast Activator.CreateInstance(...) at a moment when it is null. This gets the same error: BasePageItem pageItem = Activator.CreateInstance(t, new object[] { this }) as BasePageItem;
Edward Tanguay