CSV をクラスにバインドする (C#)
例えば
RowId,Id,StartDate,StopDate,CustomerId
0,C0001,2020-03-01T00:00:00.0000000,2020-04-01T00:00:00.0000000,0
1,C0002,2020-03-01T00:00:00.0000000,1
みたいな CSV があって
publicclassContract{publicintRowId{get;set;}publicstringId{get;set;}publicDateTimeStartDate{get;set;}publicDateTime?StopDate{get;set;}publicintCustomerId{get;set;}}にバインドしたいという話です. CSV パーサは今回は話題にしたくないので、string.Split(",")で片付くということにしましょう.
以下みたいなコードを書くと、var contracts = Load<Contract>(fstream).ToArray();でバインドできます.
publicstaticTBind<T>(Dictionary<string,int>headerMap,string[]values)whereT:class,new(){varresult=newT();foreach(varpintypeof(T).GetProperties()){if(!headerMap.ContainsKey(p.Name))continue;varvalue=values[headerMap[p.Name]];vartype=Nullable.GetUnderlyingType(p.PropertyType);if(type==null){type=p.PropertyType;}else{if(value==""){p.SetValue(result,null);continue;}}if(type==typeof(int)){p.SetValue(result,int.Parse(value));}elseif(type==typeof(bool)){p.SetValue(result,bool.Parse(value));}elseif(type==typeof(string)){p.SetValue(result,value);}elseif(type==typeof(DateTime)){if(!DateTime.TryParseExact(value,"o",null,DateTimeStyles.RoundtripKind,outvar_)){value=DateTime.Parse(value).ToString("o");}p.SetValue(result,DateTime.ParseExact(value,"o",null,DateTimeStyles.RoundtripKind));}else{thrownewApplicationException($"Unsupported type: {typeof(T).Name}");}}returnresult;}publicstaticIEnumerable<T>Load<T>(Streamstream,Encodingencoding=null)whereT:class,new(){if(encoding==null)encoding=Encoding.UTF8;using(varreader=newStreamReader(stream,encoding)){varheader=reader.ReadLine().Split(",");varheaderMap=newDictionary<string,int>();for(vari=0;i<header.Length;i++){headerMap[header[i]]=i;}while(true){varline=reader.ReadLine();if(line==null)break;yieldreturnBind<T>(headerMap,line.Split(","));}}}なお、プロパティではなくフィールドにしたい場合には .GetProperties()が .GetFields()になり、.PropertyTypeが .FieldTypeになります.
aまた、class ではなく struct にしたい場合には、where T : class, new()が where T : structになり、var result = new T();が var result = default(T);になり、p.SetValue(result, v);が p.SetValueDirect(__makeref(result), v);になります.
関連記事: クラスを CSV にデバインドする (C#)