【问题标题】:IO Monad - stop and return default(T) when condition not satisfied in Linq?IO Monad - 当 Linq 中的条件不满足时停止并返回默认值(T)?
【发布时间】:2017-06-13 21:49:30
【问题描述】:

以下代码中的sum() 已经得到a、b、c 和 10 的总和,即使 a < b(它会打印“我们搞砸了!”以表示表达式虽然不符合条件)。我尝试将Where<T>(...) 扩展方法更改为

public static IO<T> Where<T>(this IO<T> t, Func<T, bool> foo)
{
    return () => (foo(t()) ? t : Combine(() => default(T), () => { Console.WriteLine("We screwed up!"); }))();
}

但它会在select a + b + c + x 上得到 NullReferenceException 异常?为什么? 没有创建空值?

class Program
{
    static void Main(string[] args)
    {
        var A = new Monad.IO<int>(() => { Console.WriteLine("A"); return 6; });
        var B = new Monad.IO<int>(() => { Console.WriteLine("B"); return 7; });
        var C = new Monad.IO<int>(() => { Console.WriteLine("C"); return 10; });

        var sum =
            from a in A
            from b in B
            from c in C
            let x = 10
            where a > b // Should fail the whole expression and return default(T) because a < b
            select a + b + c + x;
        Console.WriteLine(sum());
    }
}

static class Monad
{
    public delegate T IO<out T>();

    public static IO<R> SelectMany<S, C, R>(this IO<S> A, Func<S, IO<C>> foo, Func<S, C, R> bar)
    {
        var a = A();
        var B = foo(a);
        var b = B();
        var c = bar(a, b);
        return () => c;
        // return () => bar(a, foo(a)());
    }

    public static IO<T> Combine<T>(IO<T> a, Action b)
    {
        return new Func<IO<T>, IO<T>>(x => { b(); return x; })(a);
    }

    public static IO<T> Where<T>(this IO<T> t, Func<T, bool> foo)
    {
        return () => (foo(t()) ? t : Combine(t, () => { Console.WriteLine("We screwed up!"); }))();
    }

    public static IO<R> Select<S, R>(this IO<S> s, Func<S, R> foo)
    {
        return () => new Func<IO<S>, R>(x => foo(x()))(s);
    }
}

代码来自https://blogs.msdn.microsoft.com/alazarev/2017/05/20/fun-with-pure-c-and-monads/

【问题讨论】:

    标签: c# linq monads


    【解决方案1】:

    在查询中,foot(t()) 是假的,所以你取的是三进制的后半部分,所以你正在返回(在Combine 运行之后)default(T) 其中T 是一个匿名类型,所以你是返回null。

    请注意,from a in A from b in B 被转换为对 SelectMany 的调用,该调用创建了一个新的匿名类型 new { a, b },然后将其传递给另一个 SelectMany 以处理 from c in C,从而产生另一个匿名类型 new { new { a, b }, c } .然后let 导致对Select 的调用返回另一个匿名类型new { new { a, b }, c}, x },这是传递给Where 和default(T),其中T 是匿名类型是null。

    【讨论】:

    • 但是 T 是 int 吗?
    • 不是在你通过SelectMany之后。
    • 我在Where 方法中添加了打印typeof(T),它得到了&lt;&gt;f__AnonymousType22[f__AnonymousType12[&lt;&gt;f__AnonymousType02[System.Int32,System.Int32],System.Int32], System.Int32]`
    • 正是——这就是new { new { new { a, b }, c }, x }的类型。
    • 所以类型参数T可以是不同的类型,取决于不同的位置。这有点反直觉。
    猜你喜欢
    • 2021-05-03
    • 2020-01-11
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多