۱۳۹۰/۰۵/۲۹

ايجاد ليستي از كلاسي جنريك

كلاس جنريك زير را در نظر بگيريد:
public class Column<T>
{
     public string Name { set; get; }
     public T Data { set; get; }
}

مشكلي كه با اين نوع كلاس‌ها وجود دارد اين است كه نمي‌توان مثلا ليست زير را در مورد آن‌ها تعريف كرد:

IList<Column<T>> myList = new List<Column<T>>();


به عبارتي مي‌خواهيم يك ليست از كلاسي جنريك داشته باشيم. راه حل انجام آن به صورت زير است:

using System.Collections;

namespace Tests
{
    public interface IColumn
    {
        string Name { set; get; }
        object Data { set; get; }
    }

    public class Column<T> : IColumn
    {
        public string Name { set; get; }

        public T Data { set; get; }

        object IColumn.Data
        {
            get { return this.Data; }
            set {  this.Data = (T)value; }
        }
    }
}

ابتدا يك اينترفيس عمومي را همانند اعضاي كلاس Column تعريف مي‌كنيم كه در آن بجاي T از object‌ استفاده شده است. سپس يك پياده سازي جنريك از اين اينترفيس را ارائه خواهيم داد؛ با اين تفاوت كه اينبار خاصيت Data مربوط به اينترفيس، به صورت خصوصي و صريح با استفاده از IColumn.Data تعريف مي‌شود و نمونه‌ي جنريك هم نام آن، عمومي خواهد بود.
اكنون مي‌توان نوشت:

var myList = new List<IColumn>();

براي مثال در اين حالت تعريف ليست زير كه از تعدادي وهله‌ي كلاسي جنريك ايجاد شده، كاملا مجاز مي‌باشد:

var myList = new List<IColumn>
{
    new Column<int> { Data = 1, Name = "Col1"},
    new Column<double> { Data = 1.2, Name = "Col2"}
};

خوب، تا اينجا يك مرحله پيشرفت است.اكنون اگر بخواهيم در اين ليست، Data مثلا عنصري را كه نامش Col1 است، دريافت كنيم چه بايد كرد؟ آن هم نه به شكل object بلكه از نوع T مشخص:

static T GetColumnData<T>(IList<IColumn> list, string name)
{
    var column = (Column<T>)Convert.ChangeType(list.Single(s => s.Name.Equals(name)), typeof(Column<T>), null);
    return column.Data;
}

و نمونه‌اي از استفاده آن:

int data = GetColumnData<int>(myList, "Col1");