4

私は最近、LINQtoXMLの使用に関する悪名高いJonSkeetからの投稿に出くわしました。この特定のコードスニペットが私の目に留まりました。

// Customers is a List<Customer>
XElement customersElement = new XElement("customers",
    customers.Select(c => new XElement("customer", //This line is "magic"
        new XAttribute("name", c.Name),
        new XAttribute("lastSeen", c.LastOrder)
        new XElement("address",
            new XAttribute("town", c.Town),
            new XAttribute("firstline", c.Address1),
            // etc
    ));

foreachループを次のように設定したアプリケーションで自分でテストすることにしました。

foreach (var kvp in m_jobs) {    //m_jobs is a Dictionary<string,Job>
    m_xmlDoc.Root.Element("SCHED_TABLE").Add(
        kvp.Value.GenerateXmlNode())
    );
}

私は次のように変更しました:

m_xmlDoc.Root.Element("SCHED_TABLE").Add(
    m_jobs.Select(job => job.Value.GenerateXmlNode())
};

ここで、GenerateXmlNode()は、特定のジョブ項目に適切なXMLマークアップを生成するメソッドです。何が起こるかはわかりませんでしたが、foreachループとまったく同じように機能することを確認してください。よくわからないのはなぜ?!また、これはLINQの「悪用」または「機能」と見なされますか?

明確にするために編集: .Selectは、私が要求したものを正確に含むIEnumerableを返すことを知っていますが、明示的に列挙していません。.Addは可変数の引数を受け入れるため、どのように機能するかを理解していますが、繰り返しになりますが、これらの引数を渡すために明示的に列挙していません。それで...それはまだどのように機能しますか?

4

4 に答える 4

7

XElement.Addメソッドは、内部では次のようになります。

public void Add(object content)
{
    if (content is IEnumerable)
    {
        foreach (object child in (IEnumerable)content)
            Add(child);
    }
    else
    {
        //process individual element
    }
}

したがって、のパブリックインターフェイスからは明確ではありませんがAdd、一連のアイテムまたは単一のアイテムのいずれかを渡すことができ、実行時にどちらであるかを判断し、それに応じて動作します。

于 2013-02-19T19:51:56.200 に答える
5

魔法はありません。このメソッドはaまたは-Addのいずれかを受け入れ、内部的には、などを含むさまざまな一般的なシナリオについて各入力をチェックします。次に、シーケンスを展開し、検出した子要素/属性を追加します。LINQは(このシナリオでは)からのシーケンスを返します。これにより、全体が使用可能になります。objectparams object[]IEnumerableIEnumerableSelect

于 2013-02-19T19:48:38.873 に答える
2

Selectを実装するすべてのタイプに適用できるLINQ 拡張メソッドIEnumerable<T>です。パラメーターとして、デリゲートまたはラムダ式(ここではラムダ式)を受け入れます。このラムダ式は、コレクションの各要素に適用されるアドホック関数を定義します。これらの要素はここで表されcます。whereは、ラムダ式によって返されるアイテムのタイプをSelect生成します。言い換えると、ラムダ式(ここではCustomersからXElements)を使用して、型の要素を型の要素に変換します。IEnumerable<U>USelectTU

コンストラクターの2番目の引数XElementも列挙を受け入れるため、この「魔法」が可能です。

public XElement(
    XName name,
    Object content
)

は、とりわけcontent可能性があります。IEnumerable<XElement>名前System.Xml.Linq空間は非常に柔軟です。からの暗黙の変換もあり、最初の引数として文字列を渡すことができますstringXName

于 2013-02-19T20:01:02.540 に答える
1

実際、この質問は3つの理由で構成されています。魔法の最大の秘密が最終的に明らかになる前に、私たちは最初に魔術師のコードを破る必要があります。そうすれば、質問を通して見ることができます。

Func<Customer, XElement> selector=
    c => {
        var xe=new XElement("address",
            new XAttribute("town", c.Town),
            new XAttribute("firstline", c.Address1)
            // , etc
            );

        return
            new XElement("customer", // This line is a part of the "magic"
                new XAttribute("name", c.Name),
                new XAttribute("lastSeen", c.LastOrder),
                xe
                );
    };

XElement customersElement=new XElement("customers", customers.Select(selector)); // This line is another part of the "magic"

クラスは、、、およびのCustomerフィールドまたはプロパティ内で想定されLastOrderます。以下は、分割された質問と回答です。Address1TownName

Q1:目を引いたコードスニペットで、明示的に列挙しなくても、の要素のメンバーにIEnumerableアクセスできるのはなぜですか?

A1:要素はラムダ式で渡されます。つまり、渡される引数はSelectデリゲートです。したがって、で渡された要素のメンバーにアクセスできますc。また、デリゲート内で呼び出すコンストラクターはのオーバーロードであり、オブジェクトまたはXElement(XName name, params object[] content)のいずれかを渡すことができます。XAttributeXElement

Q2:コードの2つの異なる構文のスニペットを使用Addして、射影された列挙型のステートメントが、選択された列挙型でSelectのコンストラクターの呼び出しのように機能するのはなぜですか?XElementforeach

A2:またはは、のオーバーロードコンストラクタとから継承したメソッドのいずれIEnumerableIEnumerable<T>のオブジェクトとして渡されます。XElementAddXContainer

Q3:Q2によるとIEnumerable、オブジェクトとして渡された場合でも、なぜ列挙できるのですか?

A3 :クラスのインスタンスは、より大きな型、ジェネリック型、またはインターフェースで渡す場合でも、常にそのクラスの(n)インスタンスです。ここではインターフェースの問題は発生していませんが、そのことを理解するために[この回答]を確認することをお勧めします。のコード内でXContainer.Add(object content)、渡された引数はIEnumerable次のコードで処理されます。

IEnumerable enumerable=content as IEnumerable;

if(enumerable!=null) {
    foreach(object element in enumerable) {
        this.Add(element);
    }
}
else {
    this.AddString(GetStringValue(content));
}

ただし、contentが配列の場合、代わりにas IEnumerable次のコードで処理されます(おそらくパフォーマンスを上げるため)。

object[] objArray=content as object[];

if(objArray!=null) {
    foreach(object element in objArray) {
        this.Add(element);
    }
}

配列を渡すだけでもが呼び出されるのはなぜだろうと思うかもしれません。XContainer.Add(object content)これは、forarrayのオーバーロードが原因Addです。

public void Add(params object[] content) {
    this.Add(content);
}

今、あなたはそれがどのように行われるかを知っています。

于 2013-02-20T00:50:06.927 に答える