4

OpenXmlで作業しているときに、次の記事に出くわしました。方法:スプレッドシートドキュメント内の2つの隣接するセルをマージする(Open XML SDK)

そこにリファクタリングしたいコードサンプルがあります。その一部は次のとおりです。

// Insert a MergeCells object into the specified position.
if (worksheet.Elements<CustomSheetView>().Count() > 0)
{
     worksheet.InsertAfter(mergeCells, 
                           worksheet.Elements<CustomSheetView>().First());
}
else if (worksheet.Elements<DataConsolidate>().Count() > 0)
{
     worksheet.InsertAfter(mergeCells, 
                           worksheet.Elements<DataConsolidate>().First());
}
else if (worksheet.Elements<SortState>().Count() > 0)
{
     worksheet.InsertAfter(mergeCells, 
                           worksheet.Elements<SortState>().First());
}
//...and 5 more 

私が管理した最良の方法は、拡張メソッドです。

public static bool InsertElementAfter<T>(this Worksheet worksheet, 
                                              OpenXmlElement element)
    where T : OpenXmlElement
{
    if (!worksheet.Elements<T>().Any())
        return false;
    else
    {
        worksheet.InsertAfter(element, worksheet.Elements<T>().First());
        return true;
    }
}

しかし、その使用法は元のコードと同じくらいひどいように見えます:

if (!worksheet.InsertElementAfter<CustomSheetView>(mergeCells))
    if (!worksheet.InsertElementAfter<DataConsolidate>(mergeCells))
        if (!worksheet.InsertElementAfter<SortState>(mergeCells))
            //...and 5 more

どういうわけか型パラメーターの配列(または何か)を宣言できれば、次のようなものを書くことができます:

foreach (var T in typeParameterList)
{
    if (worksheet.InsertElementAfter<T>(mergeCells))
        break;
}

しかし、私はこれを行う方法を知りません。

だから私のオプションは何ですか?

4

3 に答える 3

4

あなたが探しているのは、彼らが C++ で「タイプリスト」と呼んでいるものです。ただし、残念ながらこれらは C# ではサポートされていません。

できることは、さまざまな型引数を持つ一連のクラスを作成し、次のように「再帰的」にすることで、この動作をシミュレートすることです。

public interface ITypelist { Type[] List { get; } }
public class Typelist<T1> : ITypelist {
    public Type[] List { get { return new Type[]{typeof(T1)}; }}
}
public class Typelist<T1, T2> : ITypelist {
    public Type[] List { get { return new Type[]{typeof(T1), typeof(T2)}; }}

}
// etc

次に、それを使用してタイプのリストを渡すことができます。

worksheet.InsertElementAfter<Typelist<T1, T2, T3>>(mergeCells)

タイプリストを実装して、さらにトリックを追加できます。たとえば、'head' (typeof(T1)) を 'tail' (残りは Typelist として) から分割し、typelist が最初のタイプのみを処理するようにすることができます。このようなトリックを使用して、リストを反復し、複数のタイプの動作を追加できます。

制限を追加できるようにインターフェースが追加されていることに注意してください。

void InsertElementAfter<T>(...) where T:ITypelist

...残念ながら、メソッドと型を「ジェネリック」としてメソッドに渡すことはできません (まだ?)。そのため、できる最善の方法は、それらを文字列として渡し、リフレクションを使用して「実際の」メソッドにすることです (MakeGenericType / ..を使用)。 .)。

最終的には、次のような大きなヘルパー クラスになります。

// ...

public class Typelist<T1, T2> : ITypelist
{
    public Type MakeGenericType(Type t)
    {
        return t.MakeGenericType(typeof(T1));
    }

    public MethodInfo MakeGenericMethod(MethodInfo method)
    {
        return method.MakeGenericMethod(typeof(T1));
    }

    public Type Head { get { return typeof(T1); } }
    public Typelist<T2> Tail { get { return new Typelist<T2>(); } }

    public Type[] List { get { return new Type[] { typeof(T1), typeof(T2) }; } }
}

// ...

public static class Ext
{
    public static void InvokeAll<T1>(this Typelist<T1> typelist, MethodInfo baseMethod, object obj, object[] pars)
    {
        typelist.MakeGenericMethod(baseMethod).Invoke(obj, pars);
        // tail so no recursion
    }

    public static void InvokeAll<T1, T2>(this Typelist<T1, T2> typelist, MethodInfo baseMethod, object obj, object[] pars)
    {
        typelist.MakeGenericMethod(baseMethod).Invoke(obj, pars);
        InvokeAll(typelist.Tail, baseMethod, obj, pars);
    }
}

問題は、これが良いアイデアかどうかです...付加価値は、型システムを活用し、ジェネリックを使用して型のリストを渡す機能を取得できることです。欠点は、多くのコードがあり、まだ持っていることです反射を使用します。

于 2013-02-19T09:38:02.393 に答える
4

このための流れるような API を作成できます。その結果、次のようなコードが可能になります。

worksheet.InsertAfter<CustomSheetView>(mergeCells)
         .Or<DataConsolidate>()
         .Or<SortState>();

この流れるような API には 2 つの利点があります。

  1. とても表情豊かです
  2. 反射せずに動作します。

API の実装には、値とOr()メソッドを保持するクラスが必要です。

public class ChainedElementInserter
{
    OpenXmlElement _element;
    Worksheet _worksheet;
    bool _previousResult;

    // ctor that initializes all three fields goes here.

    public ChainedElementInserter Or<T>()
        where T : OpenXmlElement
    {
        if (!_previousResult)
            _previousResult = _worksheet.InsertElementAfter<T>(_element);

        return this;
    }
}

拡張メソッドはこのInsertAfterチェーンを開始し、次のようになります。

public static ChainedElementInserter InsertAfter<T>(this Worksheet worksheet, 
                                  OpenXmlElement element)
    where T : OpenXmlElement
{
    return new ChainedElementInserter(
        worksheet, element, worksheet.InsertElementAfter<T>(element));
}
于 2013-02-19T09:39:20.607 に答える
2

リフレクションは、実行時に正しい型でメソッドを呼び出すのに役立ちます。

Type[] typeParamList = new Type[] { typeof(CustomSheetView), typeof(DataConsolidate) } //And 9 more

MethodInfo method = typeof(Extensions).GetMethod("InsertElementAfter");
foreach (var type in typeParamList)
{
    var genericMethod = method.MakeGenericMethod(new Type[] { type });
    genericMethod.Invoke(null, new object[] { worksheet, mergeCells });
}
于 2013-02-19T09:38:31.390 に答える