簡體   English   中英

動態創建列表類型(或其他類型的集合?)

[英]Create the List Type dynamically (or a different kind of collection?)

我正在編寫一個讀取各種CSV文件的類。 它根據Model類挑選出重要信息,其中Model類的屬性是我要獲取的列名。 例如,我可能有一個OutlookModel,其中包含FromAddress和ToAddress列。 或者我可以有一個完全不同的列的SalesforceModel。

當閱讀器類解析行和列時,它將單元格加載到模型類的實例中。 在下面的代碼中,參數className = OutlookModel。 這里最相關的代碼行是簽名和返回...

    protected void MapColumns(string row, string className, List<OutlookModel> list)
    {
        string[] cols = row.Split(',');
        // create a model to save the important columns
        var model = Activator.CreateInstance(nameSpace, nameSpace + className);
        int j = 0;
        if (cols.Length > 0)
        {
            foreach (var c in cols)
            {
                // is this column index one of our important columns?
                if (Ordinals.ContainsKey(j))
                {
                    // this is a column we care about, so set the model property
                    model.GetType().GetProperty(Ordinals[j]).SetValue(model, c);
                }
                j++;
            }
        }
        list.Add(model);
    }

我遇到的問題是模型對象的集合。 如果我在參數中將對象定義為List <OutlookModel>,則該方法不可擴展。 如果我將其定義為List <object>,那么(我認為)我必須轉換內部列表以使用我的屬性,這些屬性在模型之間都不同。

我是C#的新手。 是否有更好的方法將這些不同的模型類型捕獲到列表/數組/集合/任何對象中,以便隨后將邏輯應用於列表?

因此,首先,我建議添加一個自定義屬性,以標記要從csv讀取的屬性,這樣,當您以后需要添加某些內容並且不必依賴過多的內容時,就不會遇到任何問題魔術弦。 這是我的測試設置:

    class ReadFromCsvAttribute : Attribute { }

    class OutlookModel
    {
        public int DontSetThisValueFromCsv { get; set; }

        [ReadFromCsv]
        public string FromAddress { get; set; }

        [ReadFromCsv]
        public string ToAddress { get; set; }
    }

    class SalesForceModel
    {
        [ReadFromCsv]
        public string Name { get; set; }

        [ReadFromCsv]
        public string Age { get; set; }
    }

    static void Main(string[] args)
    {
        string outlookSample = "Id,FromAddress,ToAddress,Useless\r\n" +
                               "1,a@b.com,c@d.com,asdf\r\n" +
                               "3,y@z.com,foo@bar.com,baz";

        string salesForceSample = "Id,Name,Age\r\n" +
                                  "1,John,30\r\n" +
                                  "2,Doe,100";

        var outlook = ReadFromCsv<OutlookModel>(outlookSample);

        var salesForce = ReadFromCsv<SalesForceModel>(salesForceSample);

    }

我將這種通用方法放在一起,以從數據中讀取所需的任何模型:

static List<T> ReadFromCsv<T>(string data)
{
    var objs = new List<T>();
    var rows = data.Split(new[] {"\r\n"}, StringSplitOptions.None);

    //create index, header dict
    var headers = rows[0].Split(',').Select((value, index) => new {value, index})
        .ToDictionary(pair => pair.index, pair => pair.value);

    //get properties to find and cache them for the moment
    var propertiesToFind = typeof (T).GetProperties().Where(x => x.GetCustomAttributes<ReadFromCsvAttribute>().Any());

    //create index, propertyinfo dict
    var indexToPropertyDict =
        headers.Where(kv => propertiesToFind.Select(x => x.Name).Contains(kv.Value))
            .ToDictionary(x => x.Key, x => propertiesToFind.Single(p => p.Name == x.Value));

    foreach (var row in rows.Skip(1))
    {
        var obj = (T)Activator.CreateInstance(typeof(T));

        var cells = row.Split(',');
        for (int i = 0; i < cells.Length; i++)
        {
            if (indexToPropertyDict.ContainsKey(i))
            {
                //set data
                indexToPropertyDict[i].SetValue(obj, cells[i]);
            }
        }
        objs.Add(obj);
    }

    return objs;
}

這是另一個示例。 由於您是C#的新手,因此我盡量避免使用linq和擴展方法。 只需將其復制到控制台應用程序中並運行即可。

另外,我喜歡Hennyy建議使用.net屬性來描述一個類,但前提是要完全控制自己的生態系統。

public class Account
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public class LastNameAccount
{
    public string LastName { get; set; }
    public string Address { get; set; }
}

public class Program
{
    public static void Main(string[] args)
    {
        Test1();
    }

    private static void Test1()
    {
        /*
         * defines the result of your CSV parsing. 
         */
        List<string> csvColumns = new List<string> { "FirstName", "LastName" };
        List<List<string>> csvRows = new List<List<string>>() {
            new List<string>(){"John","Doe"},
            new List<string>(){"Bill", "Nie"}
        };

        //Map the CSV files to Account type and output it
        var accounts = Map<Account>(csvColumns, csvRows);
        if (accounts != null)
        {
            foreach (var a in accounts)
            {
                Console.WriteLine("Account: {0} {1}", a.FirstName, a.LastName);
            }
        }

        //Map the CSV files to LastNameAccount type and output it
        var accounts2 = Map<LastNameAccount>(csvColumns, csvRows);
        if (accounts2 != null)
        {
            foreach (var a in accounts2)
            {
                Console.WriteLine("Last Name Account: {0} {1}", a.LastName, a.Address);
            }
        }
    }


    private static List<T> Map<T>(List<string> columns, List<List<string>> rows)
        where T : class, new()
    {
        //reflect the type once and get valid columns
        Type typeT = typeof(T);
        Dictionary<int, PropertyInfo> validColumns = new Dictionary<int, PropertyInfo>();
        for (int columnIndex = 0; columnIndex < columns.Count; columnIndex++)
        {
            var propertyInfo = typeT.GetProperty(columns[columnIndex]);
            if (propertyInfo != null)
            {
                validColumns.Add(columnIndex, propertyInfo);
            }
        }

        //start mapping to T 
        List<T> output = null;
        if (validColumns.Count > 0)
        {
            output = new List<T>();
            foreach (var row in rows)
            {
                //create new T
                var tempT = new T();

                //populate T's properties
                foreach (var col in validColumns)
                {
                    var propertyInfo = col.Value;
                    var columnIndex = col.Key;

                    propertyInfo.SetValue(tempT, row[columnIndex]);
                }

                //add it
                output.Add(tempT);
            }
        }

        return output;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM