玩转匿名类型

8

来自 Jon Skeet 的精彩书籍 C#深度剖析,第一版:

class Film
{
    public string Name { get; set; }
    public int Year { get; set; }

    public override string ToString()
    {
        return string.Format("Name={0}, Year={1}", Name, Year);
    }
}

var films = new List<Film>
{
    new Film {Name="Jaws", Year=1975},
    new Film {Name="Singing in the Rain", Year=1952},
    new Film {Name="Some Like It Hot", Year=1959},
    new Film {Name="The Wizard of Oz", Year=1939},
    new Film {Name="It's a Wonderful Life", Year=1946},
    new Film {Name="American Beauty", Year=1999},
    new Film {Name="High Fidelity", Year=2000},
    new Film {Name="The Usual Suspects", Year=1995}
};

Action<Film> print = film => { Console.WriteLine(film); };
films.ForEach(print);
films.FindAll(film => film.Year < 1960)
.ForEach(print);
films.Sort((f1, f2) => f1.Name.CompareTo(f2.Name));
films.ForEach(print);

上面的代码片段后面跟着一个段落。

列表9.4的前半部分只涉及数据的设置。我本来想使用匿名类型,但是从匿名类型实例的集合创建通用列表相对棘手。(您可以通过创建一个通用方法,将数组转换为同一类型的列表,然后将隐式类型的数组传递给该方法来完成它。.NET 3.5中的扩展方法ToList也提供了这个功能,但那样做是作弊的,因为我们还没有看过扩展方法!)

而上面提供的代码片段是书中第9.4节的示例。

我的问题:我正在尝试手动执行上面段落中概述的技术(查看斜体文本),但我无法完全理解他的意思。

我尝试了类似于这样的东西,但它不起作用(我也没指望它会):

using System;
using System.Collections.Generic;

namespace ScratchPad
{

class Film
{
    public string Name { get; set; }
    public int Year { get; set; }

    public override string ToString()
    {
        return string.Format("Name = {0}\tYear = {1}", 
            Name, Year);
    }
}

class Program
{
    static void Main(string[] args)
    {
        ToList<Film>( new[]
        {
            new { Name = "North By Northwest", Year = 1959 },
            new { Name = "The Green Mile", Year = 1999},
            new { Name = "The Pursuit of Happyness", Year = 2006}
        }).ForEach( f => {Console.WriteLine(f);} );

        Console.ReadKey();
    }

    static List<T> ToList<T>(
        System.Collections.IEnumerable list)
    {
        var newList = new List<T>();

        foreach (var thing in list)
            if (thing is T)
                newList.Add((T)thing);

        return newList;

    }
}

注意: 我知道IEnumerable.ToList()扩展方法并且已经使用过许多次。但我想尝试手动实现段落中概述的技术。

此外,我对在Linq之外使用匿名类型作为语法方便的情况很感兴趣,下面给出了这样一个场景。在C# 4中,我可以始终使用dynamic,并接受匿名类型作为参数,并知道我期望的工作方式。我希望我能在C# 3中做到这一点。类似于以下内容:

using System;
using Microsoft.CSharp.RuntimeBinder;

namespace PlayWithAnonType
{
    class Program
    {
        static void Main(string[] args)
        {
            PrintThingy(new { Name = "The Secret", 
Genre = "Documentary", Year = 2006 });
            Console.ReadKey();
        }

    static void PrintWhatever(dynamic whatever)
    {
        // the anonymous type's ToString() will print
        Console.WriteLine(whatever);
    }

    static void PrintThingy(dynamic thingy)
    {
        try
        {
            // I know what the thingy is
            Console.WriteLine("Name = {0}\tGenre = {1}\tYear = {2}",
                thingy.Name, thingy.Genre, thingy.Year);
        }
        catch(RuntimeBinderException ex)
        {
#pragma warning disable 0168
            Console.WriteLine("By thingy, I really meant film. 
Sorry, I should've clarified.");
#pragma warning restore 0168
        }
    }
}

}

编辑 他们应该有一个名为jon-skeet的标签。


1
我知道谁会赢得这个问题 ;) - Skurmedel
如果有关于Jon的书的问题被问到,Jon回答了,然后其他人赢了,那将非常具有讽刺意味。 - Icemanind
3个回答

8

重点是,如果我们了解ToList,我们就可以创建列表而无需自己的Film类型。这并不意味着我们能够将匿名类型与Film类型混合使用。换句话说,我们可以这样做:

// The type of list will be List<T> where T is the anonymous type
var list = new[]
{
    new { Name = "North By Northwest", Year = 1959 },
    new { Name = "The Green Mile", Year = 1999},
    new { Name = "The Pursuit of Happyness", Year = 2006}
}.ToList();

list.ForEach(x => Console.WriteLine("{0} ({1})", x.Name, x.Year));

顺便说一下,很高兴您喜欢第一版——希望第二版不会太久就会出来 :)


仍然急切地等待第二版,我经常收到更新的消息...一直拖延。但我相信它值得等待! - Abel
@Abel:哦,我不知道他们正在发送推迟通知:( 如果有什么安慰的话,我刚刚收到了前两个第一次校对后的章节,这意味着它真的很接近了。我和你一样渴望把它发布出去,我相信你也同样渴望收到它 :) - Jon Skeet
嗨Jon,我明白如果我们能使用ToList()扩展方法会是什么样的生活。我只是想尝试一下你提到的hack,如果ToList()根本不存在。<br /> 我想要一些新行,所以让我尝试一些XHTML。 <br /> PS:当然,我很喜欢这本书。这是我第二次阅读。第一次是快速阅读。这一次我会尝试书中的每一个细节。我迫不及待地等待第二版。我正在公司进行为期54小时的C# 4培训,并将分发给答题赢家。 :-) - Water Cooler v2
@Water Cooler v2:这个hack基本上就是重新实现ToList,仅此而已 :) arootbeer的回答有适当的代码。 - Jon Skeet
我感觉自己像个白痴! :-) 我应该只是在 Reflector 中看到 ToList() 扩展的实现。无论如何,非常感谢。 - Water Cooler v2

6
实际操作如下:
public void Main (string[] args)
{
    var films = ToList(new [] {
        new {Name = "Jaws", Year = 1975},
        new {Name = "Singing in the Rain", Year = 1952},
        new {Name = "Some Like It Hot", Year = 1959},
        new {Name = "The Wizard of Oz", Year = 1939},
        new {Name = "It's a Wonderful Life", Year = 1946},
        new {Name = "American Beauty", Year = 1999},
        new {Name = "High Fidelity", Year = 2000},
        new {Name = "The Usual Suspects", Year = 1995}
    }
    );


    films.ForEach(f => Console.Write(f.Name + " - " + f.Year));

}

public List<T> ToList<T> (IEnumerable<T> list)
{
    return new List<T>(list);
}

正如其他人所提到的,我不确定这有多有用。当你编写它时,确实可以获得智能感知等功能,因此可能会节省一些打字时间? :)


哈哈!抄袭者。如果我想从反编译器中作弊,我也可以这样做。:-)但是非常感谢。 - Water Cooler v2
有点难!绿色的勾勾应该来找你吗?真是个难题。非常难。 :-) - Water Cooler v2
很高兴拿到了我的第一个绿色勾勾 :P 实际上,我是根据问题编写的代码(没有使用Reflector);我之前从未想过要这样使用匿名类型。实际上,这是一个相当酷的快捷方式。 - Matt Mills
我简直不敢相信这是你的第一个绿色勾号。我想我认识你。你不是那个来自 Channel 9 的聪明 C++ 小伙子吗? - Water Cooler v2
不,我是彻头彻尾的C#迷(到目前为止)。 - Matt Mills

4
我认为Jon所描述的对你并没有什么用处。他要表达的唯一观点是,如果不是因为创建一个`List`会有问题,他通常不会为这个例子创建一个完整的类`Film`。
编辑:该死。好吧,这次我还是会把我的答案留在这里 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接