我编写了一个解析命令行参数的程序或算法。首先,需要指定有效的参数及其属性(标志,分隔符等)。
现在,我有一个辅助函数返回:Tuple<Tuple<Argument, String>, String>
:元组中的第一个元素(也是一个元组)包含一个“键值对”,null
如果没有对可以找到。第二个元素包含一个标识为选项的字符串,分隔符为" "
(意味着该值将是命令行中的下一个元素)
该功能的代码如下:
private Tuple<Tuple<Argument, String>, Argument> f(string e, List<Argument> valid) {
var p = valid.Find( x => { return e.StartsWith( x.value ); } );
if ( p == null )
return new Tuple<Tuple<Argument, String>, Argument>( null, null );
if ( p.flag )
return new Tuple<Tuple<Argument, String>, Argument>( new Tuple<Argument, String>( p, "" ), null );
if ( p.separator.Equals(" ") && p.value.Length == e.Length )
return new Tuple<Tuple<Argument, String>, Argument>( null, p );
var si = e.IndexOf(p.separator);
if ( si != p.value.Length )
return new Tuple<Tuple<Argument, String>, Argument>( null, null );
return new Tuple<Tuple<Argument, String>, Argument>( new Tuple<Argument, String>( p, e.Substring( si + p.separator.Length ) ), null );
}
有没有办法写这个更短?我是否必须明确地将Generics放在那里?
显然,为此创建自己的类将是一种解决方案。但是不是另一个吗?
作为参考,Scala中的相同代码如下所示:
val po = valid.find(p => e.startsWith(p.value))
po match {
case Some(p) =>
if ( p.flag )
(Some((p, "")), None)
else if ( p.separator.matches(" |\t") && p.value.length == e.length )
(None, Some(p))
else {
val si = e.indexOf(p.separator)
if ( si != p.value.length )
(None, None)
else
(Some((p, e.substring(si + p.separator.length))), None)
}
case _ => (None, None)
}
另外,有没有办法以紧凑的方式发起Lists
或Tuples
?与List(1, 2, 3, 4)
或(Key, Value)
代替Tuple<KeyType, ValueType>(Key, Value)
的问候,
Danyel。
答案 0 :(得分:6)
您可以使用typedef来清理代码。从技术上讲,C#没有typedef,但您可以使用using来模拟它们。像这样:
using MyTuple = Tuple<Tuple<Argument, string>, Argument>;
using MyTuple2 = Tuple<Argument, string>;
public class MyClass {
private MyTuple f(string e, List<Argument> valid) {
var p = valid.Find( x => { return e.StartsWith( x.value ); } );
if ( p == null )
return new MyTuple( null, null );
if ( p.flag )
return new MyTuple( new MyTuple2( p, "" ), null );
if ( p.separator.Equals(" ") && p.value.Length == e.Length )
return new MyTuple( null, p );
var si = e.IndexOf(p.separator);
if ( si != p.value.Length )
return MyTuple( null, null );
return new MyTuple( new MyTuple2( p,
e.Substring( si + p.separator.Length ) ), null );
}
}
关于初始化的问题,我不确定我理解。元组已经有一个紧凑的初始化方法,使用构造函数参数。
列表可以使用初始值设定项,如下所示:
var s = new List<string> { "one", "two", "three" };
答案 1 :(得分:4)
看看你的行,说明明确声明元组类型的“丑陋”:
// ugly code
return new Tuple<Tuple<Argument, String>, Argument>( new Tuple<Argument, String>( p, "" ), null );
1) 惯用语C#可能会明确定义一个类
class ArgumentMatch {
public Argument Key;
public string Value;
public Argument Unmatched;
}
// if you define the constructor
return new ArgumentMatch(p, "", null);
// or using object initializer syntax,
return new ArgumentMatch { Key = p, Value = "" };
2)您可以编写自己的通用实用程序函数以避免显式元组声明。 C#将推断函数的类型,而不是构造函数。
// using standard static method
static Tuple<T,U> MakeTuple<T,U>(T a, U b) { return new Tuple<T,U>(a,b); }
// and extension method (I don't personally like this since it's a bit
// of a name-space pollutant)
static Tuple<T,U> Pair<T,U>(this T a, U b) { return new Tuple<T,U>(a,b); }
return MakeTuple(MakeTuple(p, ""), (Argument)null);
return p.Pair("").Pair((Argument)null);
3)F#几乎是scala示例的逐行转换。如果您对使用scala感到满意,可以考虑在.NET中使用F#。
// F# example code
match valid.find(fun x -> e.StartsWith(x.Value)) with
| Some(p) ->
if p.flag then
(p, ""),None
elif p.separator = " " && p.Value.Length = e.Length then
(None,None),Some(p)
....
答案 2 :(得分:0)
这个问题可能已经过时了。
但是,4年后,Microsoft在C#7中实现了ValueTuples。
因此,您只需返回Tuples<T1,T2>
,而不是返回(T1 a, T2 b)
。
更多细节https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#tuples
此元组使用System.ValueTuple
,它是一个结构(值类型),而不是System.Tuple
,它是一个类(引用类型)。