これを読んで頭に浮かんだ解決策は、C#の組み込みの属性サポートを利用することでした。属性は、プロパティ、フィールド、メソッド、クラスなどに追加のメタデータをタグ付けする方法であり、その後、たとえばSerialization中に他のクラスによって使用されます。あなたはそこでそれを最も頻繁に見るでしょう。
IEnumerable
オブジェクトのコレクションを取得し、ユーザーが選択した選択に基づいてファイルにデータを出力できるようにする必要があるアプリケーションを構築していました。リフレクションを介して選択肢を読み取り、指示どおりに動作できるようにする属性クラスを作成しました。例を示しましょう。
最初の属性クラス:
[System.AttributeUsage(AttributeTargets.Property)]
class ExportOptionsAttribute : System.Attribute
{
public string Header { get; set; }
public string FormatString { get; set; }
public bool Export { get; set; }
public int Order { get; set; }
/// <summary>
///
/// </summary>
/// <param name="header"></param>
public ExportOptionsAttribute(string header) : this (header, null, true)
{
}
/// <summary>
///
/// </summary>
/// <param name="header"></param>
/// <param name="formatString"></param>
/// <param name="export"></param>
public ExportOptionsAttribute(string header, string formatString, bool export)
{
this.Header = header;
this.FormatString = formatString;
this.Export = export;
this.Order = 0;
}
}
このクラスをこのように定義すると、データ クラスのプロパティを次のように装飾できます (実際のプロパティは、ビジネス用語で迷子にならないように変更されています)。
public sealed class PartsOrder
{
/// <summary>
///
/// </summary>
[ExportOptions("Customer Name", Order=0)]
public string CustomerName { get; set; }
/// <summary>
///
/// </summary>
[ExportOptions("Catalog Name", Order = 1)]
public string Catalog Name { get; set; }
/// <summary>
///
/// </summary>
[ExportOptions("Unit", Order = 2)]
public string Unit { get; set; }
/// <summary>
///
/// </summary>
[ExportOptions("Component", Order = 3)]
public string Component { get; set; }
/// <summary>
///
/// </summary>
[ExportOptions("Delivery Point", Order = 4)]
public string DeliveryPoint { get; set; }
/// <summary>
///
/// </summary>
[ExportOptions("Order Date", Order = 5)]
public string OrderDate { get; set; }
}
そのため、エクスポート ルーチンでは、変数であるプロパティ名をハードコーディングしたり、表示または非表示にするフィールドや順序に関する情報を含む複雑なデータ構造を渡したりする代わりに、次のコードを実行しました。 、リフレクションを使用して、プロパティをループし、その値を CSV ファイルに出力します。
StringBuilder outputDoc = new StringBuilder();
// loop through the headers in the attributes
// a struct which decomposes the information gleaned from the attributes
List<OrderedProperties> orderedProperties = new List<OrderedProperties>();
// get the properties for my object
PropertyInfo[] props =
(typeof(PartsOrder)).GetProperties();
// loop the properties
foreach (PropertyInfo prop in props)
{
// check for a custom attribute
if (prop.GetCustomAttributesData().Count() > 0)
{
foreach (object o in prop.GetCustomAttributes(false))
{
ExportOptionsAttribute exoa = o as ExportOptionsAttribute;
if (exoa != null)
{
orderedProperties.Add(new OrderedProperties() { OrderByValue = exoa.Order, PropertyName = prop.Name, Header = exoa.Header, Export = exoa.Export });
}
}
}
}
orderedProperties = orderedProperties.Where(op => op.Export == true).OrderBy(op => op.OrderByValue).ThenBy(op => op.PropertyName).ToList();
foreach (var a in orderedProperties)
{
outputDoc.AppendFormat("{0},", a.Header);
}
// remove the trailing commma and append a new line
outputDoc.Remove(outputDoc.Length - 1, 1);
outputDoc.AppendFormat("\n");
var PartsOrderType = typeof(PartsOrder);
//TODO: loop rows
foreach (PartsOrder price in this.Orders)
{
foreach (OrderedProperties op in orderedProperties)
{
// invokes the property on the object without knowing the name of the property
outputDoc.AppendFormat("{0},", PartsOrderType.InvokeMember(op.PropertyName, BindingFlags.GetProperty, null, price, null));
}
// remove the trailing comma and append a new line
outputDoc.Remove(outputDoc.Length - 1, 1);
outputDoc.AppendFormat("\n");
}
OrderedProperties 構造体のコードは次のとおりです。
struct OrderedProperties
{
/// <summary>
///
/// </summary>
public int OrderByValue;
/// <summary>
///
/// </summary>
public string PropertyName;
/// <summary>
///
/// </summary>
public string Header;
/// <summary>
///
/// </summary>
public bool Export;
}
ご覧のとおり、プロパティ値を抽出するロジックは、クラスの構造をまったく認識していません。作成した属性で修飾されたプロパティを見つけて、それを使用して処理を実行するだけです。
これがすべて意味をなすことを願っています。さらにヘルプや説明が必要な場合は、お気軽にお問い合わせください。