C#中的管道转发 [英] Pipe forwards in C#

查看:20
本文介绍了C#中的管道转发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

继续我的调查为了在 C# 中表达 F# 想法,我想要一个管道转发运算符.对于包含在 IEnumerable 中的任何内容,我们已经有了它,因为您可以 .NextFunc() 满足您的需求.但是,例如,如果最后有任何类似折叠的减少,则无法将其结果提供给函数.

Continuing my investigation of expressing F# ideas in C#, I wanted a pipe forward operator. For anything wrapped in a IEnumerable, we already have it, as you can .NextFunc() to your heart's content. But for example if you have any fold-like reduction at the end, you can't feed the result of that into a function.

这里有两种扩展方法,我想知道是否有其他人尝试过这个,这是否是一个好主意(现在使用 Earwicker's也许包括):

Here are two extension methods, I wondered if anyone else had tried this, and if it's a good idea or not ( now with Earwicker's Maybe included):

public static void Pipe<T>(this T val, Action<T> action) where T : class
{ if (val!=null) action(val); }

public static R Pipe<T, R>(this T val, Func<T, R> func) where T : class where R : class
{ return val!=null?func(val):null; }

然后你可以这样写:

Func<string, string[]> readlines = (f) => File.ReadAllLines(f);
Action<string, string> writefile = (f, s) => File.WriteAllText(f, s);

Action<string, string> RemoveLinesContaining = (file, text) =>
    {
        file.Pipe(readlines)
            .Filter(s => !s.Contains(text))
            .Fold((val, sb) => sb.AppendLine(val), new StringBuilder())
            .Pipe((o) => o.ToString())
            .Pipe((s) => writefile(file, s));
    };

(我知道,Filter == Where in C#,和Fold==Aggregate,但我想推出自己的,我可以做WriteAllLines,但这不是重点)

(I know, Filter == Where in C#, and Fold==Aggregate, but I wanted to roll my own, and I could have done WriteAllLines, but that's not the point)

根据 Earwicker 的评论进行更正(如果我理解正确的话).

corrections as per Earwicker's comment (if I've understood correctly).

推荐答案

我没有为原始管道烦恼,但我已经尝试将所有引用都放入 Maybe monad 中:

I haven't bothered with a raw pipe, but I have tried making all references into the Maybe monad:

public static class ReferenceExtensions
{
    public static TOut IfNotNull<TIn, TOut>(this TIn v, Func<TIn, TOut> f)
                                    where TIn : class 
                                    where TOut: class
    {
        if (v == null)
            return null;

        return f(v);
    }
}

然后假设您有一个对象模型,可以让您按名称查找 RecordCompany,然后在该 RecordCompany 中查找一个 Band,一个 Band 的成员,其中任何一个都可能返回 null,因此这可能会抛出 NullReferenceException:

Then suppose you have an object model that lets you lookup a RecordCompany by name, and then lookup a Band within that RecordCompany, a Member of the Band, and any of these might return null, so this might throw a NullReferenceException:

var pixiesDrummer = Music.GetCompany("4ad.com")
                         .GetBand("Pixes")
                         .GetMember("David");

我们可以解决这个问题:

We can fix that:

var pixiesDrummer = Music.GetCompany("4ad.com")
                         .IfNotNull(rc => rc.GetBand("Pixes"))
                         .IfNotNull(band => band.GetMember("David"));

嘿,如果这些转换中的任何一个返回 null,pixiesDrummer 将为 null.

Hey presto, if any of those transitions return null, pixiesDrummer will be null.

如果我们可以做操作符重载的扩展方法不是很好吗?

Wouldn't it be great if we could do extension methods that are operator overloads?

public static TOut operator| <TIn, TOut>(TIn v, Func<TIn, TOut> f)

然后我可以像这样将我的转换 lambda 组合在一起:

Then I could pipe together my transition lambdas like this:

var pixiesDrummer = Music.GetCompany("4ad.com")     
                     | rc => rc.GetBand("Pixes")
                     | band => band.GetMember("David");

如果 System.Void 被定义为一个类型,而 Action 真的只是 Func<..., Void>,那不是很好吗?

Also wouldn't it be great if System.Void was defined as a type and Action was really just Func<..., Void>?

更新: 我写了一个博客几乎没有关于这背后的理论.

更新 2: 原始问题的替代答案,大致是您将如何在 C# 中表达 F# 管道转发运算符?"

Update 2: An alternative answer to the original question, which is roughly "How would you express the F# pipe-forward operator in C#?"

管道转发是:

let (|>) x f = f x

换句话说,它允许您以相反的顺序编写函数及其第一个参数:参数后跟函数.它只是一个有助于提高可读性的语法助手,允许您在任何函数中使用中缀符号.

In other words, it lets you write a function and its first argument in the opposite order: argument followed by function. It's just a syntactic helper that assists with readability, allowing you to make use of infix notation with any function.

这正是 C# 中扩展方法的用途.没有它们,我们将不得不写:

This is exactly what extension methods are for in C#. Without them, we would have to write:

var n = Enumerable.Select(numbers, m => m * 2);

有了它们,我们可以写:

With them, we can write:

var n = numbers.Select(m => m * 2);

(忽略他们还让我们省略了类名的事实——这是一个额外的好处,但也可以像在 Java 中一样用于非扩展方法).

(Ignore the fact that they also let us omit the class name - that's a bonus but could also be made available for non-extension methods as it is in Java).

所以 C# 已经以不同的方式解决了同样的问题.

So C# already solves the same problem in a different way.

这篇关于C#中的管道转发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆