[英]Is there idiomatic C# equivalent to C's comma operator?
我在C#中使用了一些功能性的东西,并不断陷入List.Add
不返回更新列表的事实。
通常,我想在对象上调用一个函数,然后返回更新后的对象。
例如,如果C#具有逗号运算符,那就太好了:
((accum, data) => accum.Add(data), accum)
我可以这样写自己的“逗号运算符”:
static T comma(Action a, Func<T> result) {
a();
return result();
}
看起来可以使用,但是呼叫站点很难使用。 我的第一个例子是:
((accum, data) => comma(accum.Add(data), ()=>accum))
足够的例子! 没有其他开发人员随后出现并在代码气味中皱起鼻子的最干净方法是什么?
我知道这是流利的 。
使用扩展方法添加列表的流利示例
static List<T> MyAdd<T>(this List<T> list, T element)
{
list.Add(element);
return list;
}
我知道此线程非常旧,但是我想为以后的用户添加以下信息:
当前没有这样的运算符。 在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#发行版之前已被删除 。
这就是Concat http://msdn.microsoft.com/zh-cn/library/vstudio/bb302894%28v=vs.100%29.aspx的用途。 只需将单个项目包装在数组中即可。 功能代码不应更改原始数据。 如果性能是一个问题,而这还不够好,那么您将不再使用功能范式。
((accum, data) => accum.Concat(new[]{data}))
您自然可以在C#3.0中使用代码块来完成几乎第一个示例。
((accum, data) => { accum.Add(data); return accum; })
扩展方法可以说是最好的解决方案,但是出于完整性考虑,请不要忘记显而易见的替代方法:包装器类。
public class FList<T> : List<T>
{
public new FList<T> Add(T item)
{
base.Add(item);
return this;
}
public new FList<T> RemoveAt(int index)
{
base.RemoveAt(index);
return this;
}
// etc...
}
{
var list = new FList<string>();
list.Add("foo").Add("remove me").Add("bar").RemoveAt(1);
}
我认为制作一个不需要您编写包装器方法的包装器类答案版本会很有趣。
public class FList<T> : List<T>
{
public FList<T> 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<string>();
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<T> 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"];
当然,换行符是可选的。
修改语法有点有趣。
直接来自函数编程的另一种技术如下。 定义这样的IO结构:
/// <summary>TODO</summary>
public struct IO<TSource> : IEquatable<IO<TSource>> {
/// <summary>Create a new instance of the class.</summary>
public IO(Func<TSource> functor) : this() { _functor = functor; }
/// <summary>Invokes the internal functor, returning the result.</summary>
public TSource Invoke() => (_functor | Default)();
/// <summary>Returns true exactly when the contained functor is not null.</summary>
public bool HasValue => _functor != null;
X<Func<TSource>> _functor { get; }
static Func<TSource> Default => null;
}
并使用以下扩展方法使其成为LINQ可用的monad:
[SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")]
public static class IO {
public static IO<TSource> ToIO<TSource>( this Func<TSource> source) {
source.ContractedNotNull(nameof(source));
return new IO<TSource>(source);
}
public static IO<TResult> Select<TSource,TResult>(this IO<TSource> @this,
Func<TSource,TResult> projector
) =>
@this.HasValue && projector!=null
? New(() => projector(@this.Invoke()))
: Null<TResult>();
public static IO<TResult> SelectMany<TSource,TResult>(this IO<TSource> @this,
Func<TSource,IO<TResult>> selector
) =>
@this.HasValue && selector!=null
? New(() => selector(@this.Invoke()).Invoke())
: Null<TResult>();
public static IO<TResult> SelectMany<TSource,T,TResult>(this IO<TSource> @this,
Func<TSource, IO<T>> selector,
Func<TSource,T,TResult> projector
) =>
@this.HasValue && selector!=null && projector!=null
? New(() => { var s = @this.Invoke(); return projector(s, selector(s).Invoke()); } )
: Null<TResult>();
public static IO<TResult> New<TResult> (Func<TResult> functor) => new IO<TResult>(functor);
private static IO<TResult> Null<TResult>() => new IO<TResult>(null);
}
现在您可以使用LINQ 综合语法,因此:
using Xunit;
[Fact]
public static void IOTest() {
bool isExecuted1 = false;
bool isExecuted2 = false;
bool isExecuted3 = false;
bool isExecuted4 = false;
IO<int> one = new IO<int>( () => { isExecuted1 = true; return 1; });
IO<int> two = new IO<int>( () => { isExecuted2 = true; return 2; });
Func<int, IO<int>> addOne = x => { isExecuted3 = true; return (x + 1).ToIO(); };
Func<int, Func<int, IO<int>>> 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<Unit>, IComparable<Unit> {
[CLSCompliant(false)]
public static Unit _ { get { return _this; } } static Unit _this = new Unit();
}
public static IO<Unit> ConsoleWrite(object arg) =>
ReturnIOUnit(() => Write(arg));
public static IO<Unit> ConsoleWriteLine(string value) =>
ReturnIOUnit(() => WriteLine(value));
public static IO<ConsoleKeyInfo> ConsoleReadKey() => new IO<ConsoleKeyInfo>(() => 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 逗号操作符是容易识别它是什么:一个单子合成操作。
当人们尝试以直截了当的样式编写该片段时,理解语法的真正优点显而易见:
( 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);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.