C# 这有什么魔力?

C# 这有什么魔力?,c#,xml,linq,C#,Xml,Linq,我最近看到了臭名昭著的Jon Skeet关于LINQ到XML的使用的帖子。这段特殊的代码吸引了我的眼球: // Customers is a List<Customer> XElement customersElement = new XElement("customers", customers.Select(c => new XElement("customer", //This line is "magic" new XAttribute("nam

我最近看到了臭名昭著的Jon Skeet关于LINQ到XML的使用的帖子。这段特殊的代码吸引了我的眼球:

// 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
    ));
其中GenerateXmlNode()是一个为特定作业项生成适当XML标记的方法。我不确定会发生什么,但你瞧,它的工作原理和我的foreach循环完全一样。我不太明白的是为什么?!此外,这被认为是LINQ的“滥用”还是“特性”


为清晰起见进行编辑:我知道。Select将返回一个IEnumerable,该IEnumerable正好包含我要求的内容,但我没有明确列举它。我理解.Add是如何工作的,因为它接受数量可变的参数,但同样,我没有明确地枚举来传递这些参数。所以它是如何工作的?

没有魔法;
Add
方法接受
object
params object[]
——并在内部简单地检查每个输入是否存在一系列常见场景,包括
IEnumerable
等。然后简单地展开序列,添加发现的子元素/属性。LINQ(在本场景中)从
Select
返回一个
IEnumerable
序列,使其完全可用。

在引擎盖下,
XElement.Add
方法看起来像这样:

public void Add(object content)
{
    if (content is IEnumerable)
    {
        foreach (object child in (IEnumerable)content)
            Add(child);
    }
    else
    {
        //process individual element
    }
}
因此,虽然在
添加的公共界面中不清楚,但您可以传递一个项目序列或单个项目,它将在运行时确定它是哪一个,并相应地执行操作。

选择
是一种可应用于实现
IEnumerable
的任何类型的方法。作为参数,它接受委托或(这里是lambda表达式)。此lambda表达式定义了一个应用于集合的每个元素的特殊函数。这些元素在这里用
c
表示
Select
生成一个
IEnumerable
,其中
U
是lambda表达式返回的项的类型。换句话说,
Select
通过lambda表达式将
T
类型的元素转换为
U
类型的元素(此处为客户到XElements)

由于
XElement
构造函数的第二个参数也接受枚举,所以这种“魔力”是可能的

public XElement(
    XName name,
    Object content
)

内容
可以是一个
IEnumerable
System.Xml.Linq
名称空间非常灵活。还有一个从
string
XName
的隐式转换,允许您传递字符串作为第一个参数。

事实上,这个问题由三个原因组成。在魔术的最大秘密最终揭晓之前,我们首先需要破解魔术师的密码,以便我们能够看穿以下问题:

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"
但是,如果
content
是一个数组,而不是像IEnumerable那样的
,则将使用以下代码进行处理(可能是为了获得性能):

您可能想知道为什么即使传递数组也会调用
XContainer.Add(对象内容)
,这是因为数组的
Add
重载:

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

现在,你知道怎么做了

为什么?考虑到XElement.Add
广告的目的是做什么,这段代码有什么令人惊讶的地方呢?@JoelEtherton实际上,这是令人惊讶的。方法重载是
Add(Object)
,而不是
Add(IEnumerable)
。你怎么知道它需要一个序列或一个项目,而不仅仅是一个项目?@PaulBellora谢谢,完成了。@JoelEtherton你的观点是什么?通常,从一个方法的公共API可以明显看出它是接受序列还是单个项。有一个类型为
object
的参数来确定该类型是否实现了
IEnumerable
,如果实现了,则其行为会有所不同,这是非常奇怪和意外的。我知道我没有料到,我料到
public void Add(IEnumerable objects)
@Servy的方法会有额外的过载。我很抱歉。我粘贴了错误的链接。此链接描述了这并不奇怪的原因:。如果您使用RTM,您将看到它表明可以传入复杂的内容:
任何实现IEnumerable的类型。
。有趣的是,这仅仅是因为Add()在后台支持它,而我不希望它在其他领域工作?@Kittoes,是的,这是因为这个类中的Add非常特殊。请尽量不要根据这一个案例自己编写这样的代码-绝对没有办法根据方法签名来判断这种行为。@AlexeiLevenkov正是我的想法,如果我在方法签名中看到了IEnumerable,那么发生的事情就会非常明显。
“由于XElement构造函数的第二个参数也接受枚举,所以这种“魔法”是可能的。”
他不是在调用XElement构造函数,而是在调用
Add
方法,该方法不接受枚举作为参数。问题是关于Add,没有接受枚举的构造函数(
params
,但不是
IEnumerable
)。在没有调用
Add
的第一个代码段中出现“魔法”。
XElement
有一个构造函数重载:
public XElement(XName名称,对象内容)
。此
内容可以是
IEnumerable
@OlivierJacot Descombes第二个和第三个片段与第三个片段完全无关。我专门使用
。在
.Add
语句中选择
,尽管Add()没有接受IEnumerable的重载。代码运行正常。在dotPeek中反编译System.Xml.Linq后,我可以看到XContainer对象包含Add()函数,该函数的工作方式与上面的答案完全相同。它检查传递的对象是可枚举的还是数组(以及其他一些内容).好的,注释
'这行是“神奇的”
sho
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"
IEnumerable enumerable=content as IEnumerable;

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

if(objArray!=null) {
    foreach(object element in objArray) {
        this.Add(element);
    }
}
public void Add(params object[] content) {
    this.Add(content);
}