如何正确地将lambda表达式的“var”结果转换为具体类型?

时间:2010-03-26 14:16:40

标签: c# lambda type-inference

所以我想学习更多有关lambda表达式的知识。我在stackoverflow上阅读this question,同意所选答案,并尝试使用简单的LINQ表达式在C#中使用控制台应用程序来实现该算法。

我的问题是:如何将lambda表达式的“var result”转换为可用的对象,然后我可以打印结果?

我还要感谢我在宣布outer => outer.Value.Frequency

时对正在发生的事情的深入解释

(我已经阅读了很多关于lambda表达式的解释,但是另外的澄清会有所帮助)

C#
//Input : {5, 13, 6, 5, 13, 7, 8, 6, 5}

//Output : {5, 5, 5, 13, 13, 6, 6, 7, 8}

//The question is to arrange the numbers in the array in decreasing order of their frequency, preserving the order of their occurrence.

//If there is a tie, like in this example between 13 and 6, then the number occurring first in the input array would come first in the output array.

      List<int> input = new List<int>();
      input.Add(5);
      input.Add(13);
      input.Add(6);
      input.Add(5);
      input.Add(13);
      input.Add(7);
      input.Add(8);
      input.Add(6);
      input.Add(5);      

      Dictionary<int, FrequencyAndValue> dictionary = new Dictionary<int, FrequencyAndValue>();

      foreach (int number in input)
      {
        if (!dictionary.ContainsKey(number))
        {
          dictionary.Add(number, new FrequencyAndValue(1, number) );
        }
        else
        {
          dictionary[number].Frequency++;
        }
      }

      var result = dictionary.OrderByDescending(outer => outer.Value.Frequency);

      // How to translate the result into something I can print?? 

有关打印命令的答案,请参阅my answer here

4 个答案:

答案 0 :(得分:27)

  

如何将lambda表达式的“var result”转换为可用的对象,然后我可以打印结果?

首先,“lambda表达式”只是表达式a=>b形式的部分。查询的其余部分只是一个以lambda作为参数的方法调用。

无论如何,如果我能教人们关于LINQ的一件事,那就是:“结果”不是查询的结果是查询本身

如果要查看结果,请询问每个结果的查询:

foreach(var item in result)
    Console.WriteLine(item.ToString());
  

我还要感谢我在声明外部=&gt;时对正在发生的事情的深入解释。 outer.Value.Frequency

不确定。我们首先计算所涉及的所有类型。我们看到lambda是一个函数,它接受一个KeyValuePair并返回一个int,所以我们生成一个方法

static private int MyLambda(KeyValuePair<int, FrequencyAndValue> outer)
{
    return outer.Value.Frequency;
}

接下来,我们采用该方法并从中创建一个委托:

var result = dictionary.OrderByDescending(
    new Func<KeyValuePair<int, FrequencyAndValue>, int>(MyLambda));

并重写扩展方法调用:

var result = Enumerable.OrderByDescending<KeyValuePair<int, FrequencyAndValue>, int>(
    dictionary,
    new Func<KeyValuePair<int, FrequencyAndValue>, int>(MyLambda));

并重写var:

IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>> result =
    Enumerable.OrderByDescending<KeyValuePair<int, FrequencyAndValue>, int>(
    dictionary,
    new Func<KeyValuePair<int, FrequencyAndValue>, int>(MyLambda));

我希望您同意您输入的代码比这个混乱更具可读性。类型推理岩石。

结果是一个对象,它表示按给定键对此字典进行排序的能力。请仔细阅读:它表示能力按该键对字典进行排序。在你要求结果之前,它实际上并没有这样做;到目前为止,只有一个对象说“当被要求获得结果时,用这个键对字典进行排序”。

假设您要求结果。它如何计算排序列表?它询问每个元素的字典。然后它在每个元素上调用MyLambda,它返回一个整数,所以我们现在有一对字典键值对和整数。然后它构建一个按该整数排序的对列表。然后,当你要求时,它会一次一个地分发该列表的元素。

  

我们看到lambda是一个函数,它接受KeyValuePair并返回一个int“ - 你是如何确定的?我没有从方法返回值中看到它,也没有记录在OrderByDescending()中。

啊,我看到了混乱;出于教学原因,我对语义分析的确切顺序略微提出了一点。

我们如何进行此类型推断是C#中更微妙和有趣的部分之一。

这是它的工作原理。

我们看到OrderByDescending声明为:

static IOrderedEnumerable<T> OrderByDescending<T, K>(
    this IEnumerable<T> sequence, 
    Func<T, K> keyExtractor)

我们发现我们可以调用此方法:

OrderByDescending(dictionary, o=>o.Value.Frequency)

但我们不知道T和K是什么。所以我们首先看一下不是lambda的东西。你的字典实现IEnumerable<KeyValuePair<int, FrequencyOrValue>>所以我们首先说“T可能是KeyValuePair<int, FrequencyOrValue>”。

在这一点上,我们没有别的东西可以从不是lambdas的东西中推断出来,所以我们开始看看lambdas。我们看到我们有一个lambda o=>o.Value.Frequency,到目前为止我们已经确定keyExtractor的类型是Func<KeyValuePair<int, FrequencyOrValue>, K>,我们仍在寻找K.所以我们假设lambda实际上是:

(KeyValuePair<int, FrequencyOrValue> o)=>{return o.Value.Frequency;}

我们要求绑定吗?是!是的,它确实。我们可以成功地编译这个lambda而没有错误,当我们这样做时,我们看到它的所有返回语句都返回一个int。

因此我们推断K是int,我们现在对整个事物进行完整的类型分析。

这是一个相当直接的推论;他们可以变得更加怪异。如果此主题特别引起您的兴趣,请参阅我博客上的“类型推断”存档。

http://blogs.msdn.com/ericlippert/archive/tags/Type+Inference/default.aspx

特别是,这是我的视频,解释了上面的内容以及其他一些有趣的案例:

http://blogs.msdn.com/ericlippert/archive/2006/11/17/a-face-made-for-email-part-three.aspx

答案 1 :(得分:2)

OrderByDescending Function将返回一个IEnumerable,实际上是一个IOrderedEnumerable,其中TSource是原始可枚举的类型源。

当您使用Dictionary时,OrderByDescending将返回:

 IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>>

对象,将根据提供的表达式进行排序。

答案 2 :(得分:1)

var result = dictionary.OrderByDescending(outer => outer.Value.Frequency);

此行为您提供IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>>调用结果。至于lambda,它的类型为

Func<KeyValuePair<int,FrequencyAndValue>, int>

这意味着它接受KeyValuePair<int, FrequencyAndValue>参数(您正在调用外部参数),并返回与该对的值的Frequency属性对应的整数。因此,生成的IOrderedEnumerable按频率按相反的顺序排序。

答案 3 :(得分:0)

为了完整答案文档,我使用更通用的“item”以及更具体的IOrderedEnumerable打印输出。

C#
static void Main(string[] args)
    {

      //Input : {5, 13, 6, 5, 13, 7, 8, 6, 5}

      //Output : {5, 5, 5, 13, 13, 6, 6, 7, 8}

      //The question is to arrange the numbers in the array in decreasing order of their frequency, preserving the order of their occurrence.

      //If there is a tie, like in this example between 13 and 6, then the number occurring first in the input array would come first in the output array.

      List<int> input = new List<int>();
      input.Add(5);
      input.Add(13);
      input.Add(6);
      input.Add(5);
      input.Add(13);
      input.Add(7);
      input.Add(8);
      input.Add(6);
      input.Add(5);      

      Dictionary<int, FrequencyAndValue> dictionary = new Dictionary<int, FrequencyAndValue>();

      foreach (int number in input)
      {
        if (!dictionary.ContainsKey(number))
        {
          dictionary.Add(number, new FrequencyAndValue(1, number) );
        }
        else
        {
          dictionary[number].Frequency++;
        }
      }

      var result = dictionary.OrderByDescending(outer => outer.Value.Frequency);

      // BEGIN Priting results with the help of stackoverflow answers 
      Console.Write("With Items: ");    
      foreach (var item in result)
      {
        for (int i = 0; i < item.Value.Frequency; i++)
        {
          Console.Write(item.Value.Value + " ");
        }
        //Console.WriteLine(item.Value.Frequency + " " + item.Value.Value);
      }
      Console.WriteLine();

      Console.Write("With IOrderedEnumerable: ");    
      IOrderedEnumerable<KeyValuePair<int, FrequencyAndValue>> myres = result;
      foreach (KeyValuePair<int, FrequencyAndValue> fv in myres)
      {
        for(int i = 0; i < fv.Value.Frequency; i++ )
        {
          Console.Write(fv.Value.Value + " ");
        }
      }
      Console.WriteLine();
      // END Priting results with the help of stackoverflow answers 
      Console.ReadLine();
    }
    class FrequencyAndValue
    {
      public int Frequency{ get; set;}
      public int Value{ get; set;}
      public FrequencyAndValue(int myFreq, int myValue)
      {
        Value = myValue;
        Frequency = myFreq;
      }
    }
}