views:

66

answers:

2

I have a number of "section items" (Lesson, Info) which inherit from the common type SectionItem. The various types of SectionItems share some but not all properties.

I have found the best way to pass parameters to each kind of object is to pack them all in a Dictionary<string, object> and then let the base class SectionItem unpack the common ones, and each inheriting class unpack the specific ones.

This works well enough, but this is all very C#2 since I will only catch errors at runtime and not during compilation. Is there a way to do this more elegantly perhaps with generics?

alt text

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

namespace TestPass234
{
    class Program
    {
        static void Main(string[] args)
        {
            List<SectionItem> sectionItems = new List<SectionItem>();

            {
                Dictionary<string, object> vars = new Dictionary<string, object>();
                vars.Add("sectionNumber", 1);
                vars.Add("title", "Lesson #1");
                vars.Add("startDate", new DateTime(2008, 12, 25));
                List<Flashcard> flascards = new List<Flashcard>();
                flascards.Add(new Flashcard { Question = "What color is the sky?", Answer = "blue" });
                flascards.Add(new Flashcard { Question = "What color is the sun?", Answer = "yellow" });
                vars.Add("flashcards", flascards);
                SectionItem sectionItem = SectionItem.Instantiate("lesson", vars);
                sectionItems.Add(sectionItem);
            }

            {
                Dictionary<string, object> vars = new Dictionary<string, object>();
                vars.Add("title", "Info #1");
                vars.Add("content", "This is info number one.");
                SectionItem sectionItem = SectionItem.Instantiate("info", vars);
                sectionItems.Add(sectionItem);
            }

            foreach (var sectionItem in sectionItems)
            {
                Console.WriteLine(sectionItem.Render());
            }
            Console.ReadLine();
        }
    }

    public class SectionItem
    {
        protected string _title;

        public SectionItem()
        { }

        public SectionItem(Dictionary<string, object> vars)
        {
            _title = Convert.ToString(vars["title"]);
        }

        public static SectionItem Instantiate(string idCode, Dictionary<string, object> vars)
        {
            switch (idCode)
            {
                case "lesson":
                    return new SectionItemLesson(vars);
                case "info":
                    return new SectionItemInfo(vars);
                default:
                    return new SectionItem();
            }
        }

        public virtual string Render()
        {
            return "undefined section item";
        }

    }


    public class SectionItemLesson : SectionItem
    {
        private int _sectionNumber;
        private DateTime _startDate;
        private List<Flashcard> _flashcards = new List<Flashcard>();

        public SectionItemLesson(Dictionary<string, object> vars) : base(vars)
        {
            _sectionNumber = Convert.ToInt32(vars["sectionNumber"]);
            _startDate = Convert.ToDateTime(vars["startDate"]);
            _flashcards = vars["flashcards"] as List<Flashcard>;
        }

        public override string Render()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(String.Format(">>> {0}. {1} (Starts {2:ddd, MMM d, yyyy})", _sectionNumber, _title, _startDate));
            foreach (var flashcard in _flashcards)
                sb.AppendLine("    - " + flashcard.Render());
            return sb.ToString();
        }
    }

    public class SectionItemInfo : SectionItem
    {
        private string _content;

        public SectionItemInfo(Dictionary<string, object> vars)
            : base(vars)
        {
            _content = Convert.ToString(vars["content"]);
        }

        public override string Render()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(String.Format(">>> {0}", _title));
            sb.AppendLine(String.Format("    {0}", _content));
            return sb.ToString();
        }
    }

    public class Flashcard
    {
        public string Question { get; set; }
        public string Answer { get; set; }

        public string Render()
        {
            return "Q: " + Question + " A: " + Answer;
        }
    }
}
A: 

If the set of possible names is known at compile time and using C#4 you could use default/optional parameters on the constructor. If only certain combinations are valid (e.g. only supply "Foo" if neither "Bar" and "Baz" are supplied) then you will still need runtime checks.

But if the set of names is truly dynamic, then there are only the options of a dictionary or anonymous type (and use reflection to extract the set of names in the body).

Richard
+2  A: 

Can you just create a 'parameters' class for each SectionItem class?

public class SectionItemParameters
{
    public string Title { get; set; }
}

public class SectionItemLessonParameters
    : SectionItemParameters
{
    public int SectionNumber { get; set; }
    public DateTime StartDate { get; set; }
    public List<Flashcard> Flashcards { get; set; }
}

public class SectionItemInfoParameters
    : SectionItemParameters
{
    public string Content { get; set; }
}

Then, every class in the hierarchy can receive its parameters in a strongly typed object. Your factory method Instantiate would take in a SectionItemParameters and cast to the appropriate type for the constructor being called.

Quartermeister
Edward Tanguay