【问题标题】:Understanding Delegates and Task了解委托和任务
【发布时间】:2015-07-30 09:34:48
【问题描述】:
public static Task<TResult> ForEachParallel<TItem, TSubResult, TResult, TParam>(this IEnumerable items, Func<TItem, TParam, TSubResult> map, Func<TSubResult[], TResult> reduce, TParam param)
{
    if (items == null) { throw new ArgumentNullException("items"); }
    if (map == null) { throw new ArgumentNullException("map"); }
    if (reduce == null) { throw new ArgumentNullException("reduce"); }

    return Task<TResult>.Factory.StartNew(() =>
    {
        List<Task<TSubResult>> tasks = new List<Task<TSubResult>>();

        foreach (TItem item in items)
        {
            Task<TSubResult> t = Task<TSubResult>.Factory.StartNew(item2 =>
            {
                var mparam = (Tuple<TItem, TParam>)item2;
                return map(mparam.Item1, mparam.Item2);
            },
                new Tuple<TItem, TParam>(item, param),
                TaskCreationOptions.None | TaskCreationOptions.AttachedToParent);
            tasks.Add(t);
        }

        List<TSubResult> results = new List<TSubResult>();
        foreach (Task<TSubResult> task in tasks)
        {
            results.Add(task.Result);
        }

        return reduce(results.ToArray());
    });
}

static void Main(string[] args)
{
    var a = Generate().ForEachParallel<int, int, int, int>(
        (element, param) =>
        {
            var result = element * param;
            Console.WriteLine("Map: {0}, {1}", result, Task.CurrentId);
            Thread.Sleep(new Random(DateTime.Now.Millisecond).Next(500));
            return result;
        },
        (subresult) =>
        {
            var sum = 0;
            foreach (var item in subresult)
            {
                        sum += item;
                    }
                    Console.WriteLine("Reduce: {0}", sum);
                    return sum;
                },
                5);

            Console.WriteLine(a.Result);
        }
}

static IEnumerable Generate()
{
    for (int i = 0; i < 100; i++)
    {
        yield return i;
    }
}

我有几个关于该计划的问题:

  1. main()中,为什么作者没有填充4个值 (即,元素,参数,子结果)?
  2. main() 中的元素和参数值是什么?
  3. ForEachParallel()中,new Tuple&lt;TItem, TParam&gt;(item, param)的作用是什么?
  4. ForEachParallel()var mparam = (Tuple&lt;TItem, TParam&gt;)item2中,item2是什么?作者是在调用元组吗 功能?

【问题讨论】:

  • “作者”是什么意思?什么是“即”?
  • 它是来自博客的示例应用程序,即“它们如下”,为什么作者没有传递第四个参数

标签: c# .net c#-4.0 delegates


【解决方案1】:

用一个简单的例子来解释这一点是最容易的。

这段代码:

private static string UppercaseString(string inputString)
{
    return inputString.ToUpper();
}

public static void Main()
{
    var result = UppercaseString("hello");
}

完全一样:

private static string UppercaseString(string inputString)
{
    return inputString.ToUpper();
}

public static void Main()
{
    Func<string, string> convert = UppercaseString;
    var result = convert("hello");
}

和完全一样:

public static void Main()
{
    Func<string, string> convert = inputString => inputString.ToUpper();
    var result = convert("hello");
}

第一个例子是传统的做事方式。

第二个示例使用Func&lt;string, string&gt; 创建指向方法的指针(委托)。

第三个示例使用 lambda 表达式。

在所有情况下,inputString 都是方法的参数。调用方法时inputString的值设置为"hello"

您的示例代码中的elementparamsubresultitem2 也是如此。它们都是代表的参数。无论调用这些代表什么都负责输入参数。

可能使您的代码更难理解的是委托在其他方法中用作参数。例如:

private static string Hello(Func<string, string> func)
{
    return func("hello");
}

public static void Main()
{
    Func<string, string> convert = inputString => inputString.ToUpper();
    var result = Hello(convert);
}

另请参阅有关此主题的 MSDN 文档:https://msdn.microsoft.com/en-us/library/bb549151(v=vs.110).aspx

【讨论】:

  • 非常感谢,再澄清一下,这里有什么新的 Tuple(item, param) 功能以及可能是什么:return map(mparam.Item1, mparam.Item2);?
【解决方案2】:

在深入研究这段代码的工作原理之前,了解一些非常有用的 C# 语言理论。我会在每个答案附近标出一个链接。

a).b). 在调用 lambda 表达式时,您期望填充的元素将具有值。您在 main 中看到的是 lambda 表达式,它们不是在声明时而是在使用时调用。 第一个 lambda 将在行上调用 return map(mparam.Item1, mparam.Item2); 第二个 lambda 将在行上调用 return reduce(results.ToArray()); 这个 lambda 表达式的起点可以是 here

c)。 调用 StartNew 方法时,您可以选择使用不同的方法集 (method overloading) 来调用它。所以这里调用的是这个特定的方法签名:

public Task StartNew(Action<Object> action,
                     Object state,
                     TaskCreationOptions creationOptions)

这意味着这个方法的第二个参数(状态)将是前一个动作的输入参数。

d). 所以新创建的Tuple 对象将被传递给 lambda 表达式:

item2 =>
{
    var mparam = (Tuple<TItem, TParam>)item2;
    return map(mparam.Item1, mparam.Item2);
}, 

基本上,Action&lt;object&gt; 委托表示一个方法,它接受 Object 类型的参数作为输入,但您需要一个元组来访问其项目(Item1 和 Item2)。这行var mparam = (Tuple&lt;TItem, TParam&gt;)item2 进行了从对象到元组的显式转换。

要找出第 4 个参数未传递的原因,您需要了解 Method Extensions。事实上,第一个参数没有被传递,因为该方法是通过使用 IEnumerable 类型的对象调用的。此方法在不触及原始类的情况下扩展了 IEnumerable 类,因此说“我希望此方法扩展 IEnumerable 并将其用于每个 IEnumerable 对象”的方法是将第一个参数设置为 IEnumerable。编译器将确保它将列表作为第一个参数发送,以便在扩展方法中使用它。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    相关资源
    最近更新 更多