是否有惯用的C#等同于C的逗号运算符?

我正在使用C#中的一些function,并继续坚持List.Add不返回更新列表的事实。

一般来说,我想在一个对象上调用一个函数,然后返回更新的对象。

例如,如果C#有一个逗号运算符会很棒:

 ((accum, data) => accum.Add(data), accum) 

我可以像这样编写自己的“逗号运算符”:

 static T comma(Action a, Func result) { a(); return result(); } 

看起来它会起作用,但是呼叫站点会很难看。 我的第一个例子是:

 ((accum, data) => comma(accum.Add(data), ()=>accum)) 

足够的例子! 如果没有其他开发人员出现并且在代码味道上皱起鼻子,最干净的方法是什么?

我知道这是流利的 。

使用扩展方法的List.Add的流畅示例

 static List MyAdd(this List list, T element) { list.Add(element); return list; } 

这就是Concat http://msdn.microsoft.com/en-us/library/vstudio/bb302894%28v=vs.100%29.aspx的用途。 只需将一个项目包装在一个数组中。 function代码不应该改变原始数据。 如果性能受到关注,并且这还不够好,那么您将不再使用function范例。

 ((accum, data) => accum.Concat(new[]{data})) 

我知道这个post很老了,但我想为将来的用户附加以下信息:

目前没有这样的运营商。 在C#6开发周期中,添加了semicolon operator ,如下所示:

 int square = (int x = int.Parse(Console.ReadLine()); Console.WriteLine(x - 2); x * x); 

可以翻译如下:

 int square = compiler_generated_Function(); [MethodImpl(MethodImplOptions.AggressiveInlining)] private int compiler_generated_Function() { int x = int.Parse(Console.ReadLine()); Console.WriteLine(x - 2); return x * x; } 

但是,在最终的C#版本发布之前,此function已被删除。

扩展方法可以说是最好的解决方案,但为了完整起见,不要忘记明显的替代方法:包装类。

 public class FList : List { public new FList Add(T item) { base.Add(item); return this; } public new FList RemoveAt(int index) { base.RemoveAt(index); return this; } // etc... } { var list = new FList(); list.Add("foo").Add("remove me").Add("bar").RemoveAt(1); } 

我认为制作一个我的包装类答案版本并不需要你编写包装器方法会很有趣。

 public class FList : List { public FList Do(string method, params object[] args) { var methodInfo = GetType().GetMethod(method); if (methodInfo == null) throw new InvalidOperationException("I have no " + method + " method."); if (methodInfo.ReturnType != typeof(void)) throw new InvalidOperationException("I'm only meant for void methods."); methodInfo.Invoke(this, args); return this; } } { var list = new FList(); list.Do("Add", "foo") .Do("Add", "remove me") .Do("Add", "bar") .Do("RemoveAt", 1) .Do("Insert", 1, "replacement"); foreach (var item in list) Console.WriteLine(item); } 

输出:

 foo replacement bar 

编辑

您可以通过利用C#索引属性来减少语法。

只需添加此方法:

 public FList this[string method, params object[] args] { get { return Do(method, args); } } 

电话现在看起来像:

 list = list["Add", "foo"] ["Add", "remove me"] ["Add", "bar"] ["RemoveAt", 1] ["Insert", 1, "replacement"]; 

当然,换行是可选的。

破解语法只是一点乐趣。

您可以使用C#3.0中的代码块自然地完成第一个示例。

 ((accum, data) => { accum.Add(data); return accum; }) 

直接来自函数编程的另一种技术如下。 像这样定义一个IO结构:

 /// TODO public struct IO : IEquatable> { /// Create a new instance of the class. public IO(Func functor) : this() { _functor = functor; } /// Invokes the internal functor, returning the result. public TSource Invoke() => (_functor | Default)(); /// Returns true exactly when the contained functor is not null. public bool HasValue => _functor != null; X> _functor { get; } static Func Default => null; } 

并使用这些扩展方法使其成为具有LINQfunction的monad:

 [SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")] public static class IO { public static IO ToIO( this Func source) { source.ContractedNotNull(nameof(source)); return new IO(source); } public static IO Select(this IO @this, Func projector ) => @this.HasValue && projector!=null ? New(() => projector(@this.Invoke())) : Null(); public static IO SelectMany(this IO @this, Func> selector ) => @this.HasValue && selector!=null ? New(() => selector(@this.Invoke()).Invoke()) : Null(); public static IO SelectMany(this IO @this, Func> selector, Func projector ) => @this.HasValue && selector!=null && projector!=null ? New(() => { var s = @this.Invoke(); return projector(s, selector(s).Invoke()); } ) : Null(); public static IO New (Func functor) => new IO(functor); private static IO Null() => new IO(null); } 

现在您可以使用LINQ 综合语法

 using Xunit; [Fact] public static void IOTest() { bool isExecuted1 = false; bool isExecuted2 = false; bool isExecuted3 = false; bool isExecuted4 = false; IO one = new IO( () => { isExecuted1 = true; return 1; }); IO two = new IO( () => { isExecuted2 = true; return 2; }); Func> addOne = x => { isExecuted3 = true; return (x + 1).ToIO(); }; Func>> add = x => y => { isExecuted4 = true; return (x + y).ToIO(); }; var query1 = ( from x in one from y in two from z in addOne(y) from _ in "abc".ToIO() let addOne2 = add(x) select addOne2(z) ); Assert.False(isExecuted1); // Laziness. Assert.False(isExecuted2); // Laziness. Assert.False(isExecuted3); // Laziness. Assert.False(isExecuted4); // Laziness. int lhs = 1 + 2 + 1; int rhs = query1.Invoke().Invoke(); Assert.Equal(lhs, rhs); // Execution. Assert.True(isExecuted1); Assert.True(isExecuted2); Assert.True(isExecuted3); Assert.True(isExecuted4); } 

当一个人想要一个组成但只返回void的IO monad时,定义这个struct和依赖方法:

 public struct Unit : IEquatable, IComparable { [CLSCompliant(false)] public static Unit _ { get { return _this; } } static Unit _this = new Unit(); } public static IO ConsoleWrite(object arg) => ReturnIOUnit(() => Write(arg)); public static IO ConsoleWriteLine(string value) => ReturnIOUnit(() => WriteLine(value)); public static IO ConsoleReadKey() => new IO(() => ReadKey()); 

这很容易编写代码片段,如下所示:

 from pass in Enumerable.Range(0, int.MaxValue) let counter = Readers.Counter(0) select ( from state in gcdStartStates where _predicate(pass, counter()) select state ) into enumerable where ( from _ in Gcd.Run(enumerable.ToList()).ToIO() from __ in ConsoleWrite(Prompt(mode)) from c in ConsoleReadKey() from ___ in ConsoleWriteLine() select c.KeyChar.ToUpper() == 'Q' ).Invoke() select 0; 

旧的C逗号运算符很容易被识别出来:monadic compose操作。

当一个人尝试用flunt风格编写那个片段时,理解语法的真正价值是显而易见的:

 ( Enumerable.Range(0,int.MaxValue) .Select(pass => new {pass, counter = Readers.Counter(0)}) .Select(_ => gcdStartStates.Where(state => _predicate(_.pass,_.counter())) .Select(state => state) ) ).Where(enumerable => ( (Gcd.Run(enumerable.ToList()) ).ToIO() .SelectMany(_ => ConsoleWrite(Prompt(mode)),(_,__) => new {}) .SelectMany(_ => ConsoleReadKey(), (_, c) => new {c}) .SelectMany(_ => ConsoleWriteLine(), (_,__) => _.c.KeyChar.ToUpper() == 'Q') ).Invoke() ).Select(list => 0);