tan*_*ius 83
这就是重点 - 不要一直制作自定义类或结构更方便.这是一种改进,Action
或者Func
......你可以自己制作这种类型,但它们存在于框架中很方便.
Mar*_*oVW 74
使用元组,您可以轻松实现二维字典(或者就此而言是n维字典).例如,您可以使用此类字典来实现货币交换映射:
var forex = new Dictionary<Tuple<string, string>, decimal>();
forex.Add(Tuple.Create("USD", "EUR"), 0.74850m); // 1 USD = 0.74850 EUR
forex.Add(Tuple.Create("USD", "GBP"), 0.64128m);
forex.Add(Tuple.Create("EUR", "USD"), 1.33635m);
forex.Add(Tuple.Create("EUR", "GBP"), 0.85677m);
forex.Add(Tuple.Create("GBP", "USD"), 1.55938m);
forex.Add(Tuple.Create("GBP", "EUR"), 1.16717m);
forex.Add(Tuple.Create("USD", "USD"), 1.00000m);
forex.Add(Tuple.Create("EUR", "EUR"), 1.00000m);
forex.Add(Tuple.Create("GBP", "GBP"), 1.00000m);
decimal result;
result = 35.0m * forex[Tuple.Create("USD", "EUR")]; // USD 35.00 = EUR 26.20
result = 35.0m * forex[Tuple.Create("EUR", "GBP")]; // EUR 35.00 = GBP 29.99
result = 35.0m * forex[Tuple.Create("GBP", "USD")]; // GBP 35.00 = USD 54.58
Run Code Online (Sandbox Code Playgroud)
Tej*_*ejs 23
这是一个小例子 - 假设您有一个方法需要查找用户的句柄和电子邮件地址,给定用户ID.您始终可以创建包含该数据的自定义类,或者对该数据使用ref/out参数,或者您只需返回一个元组并拥有一个很好的方法签名,而无需创建新的POCO.
public static void Main(string[] args)
{
int userId = 0;
Tuple<string, string> userData = GetUserData(userId);
}
public static Tuple<string, string> GetUserData(int userId)
{
return new Tuple<string, string>("Hello", "World");
}
Run Code Online (Sandbox Code Playgroud)
Cra*_*ntz 23
我用一个元组来解决Euler项目的问题11:
class Grid
{
public static int[,] Cells = { { 08, 02, 22, // whole grid omitted
public static IEnumerable<Tuple<int, int, int, int>> ToList()
{
// code converts grid to enumeration every possible set of 4 per rules
// code omitted
}
}
Run Code Online (Sandbox Code Playgroud)
现在我可以用以下方法解决整个问题:
class Program
{
static void Main(string[] args)
{
int product = Grid.ToList().Max(t => t.Item1 * t.Item2 * t.Item3 * t.Item4);
Console.WriteLine("Maximum product is {0}", product);
}
}
Run Code Online (Sandbox Code Playgroud)
我可以使用自定义类型,但它看起来就像Tuple.
Jul*_*iet 16
C#的元组语法非常笨重,所以元组很难说出来.并且它没有模式匹配,所以它们也很难使用.
但偶尔,您只需要一个特殊的对象分组,而无需为其创建类.例如,假设我想聚合一个列表,但我想要两个值而不是一个:
// sum and sum of squares at the same time
var x =
Enumerable.Range(1, 100)
.Aggregate((acc, x) => Tuple.Create(acc.Item1 + x, acc.Item2 + x * x));
Run Code Online (Sandbox Code Playgroud)
我们不是将一组值组合成一个结果,而是将一个结果扩展为一组值.编写此函数的最简单方法是:
static IEnumerable<T> Unfold<T, State>(State seed, Func<State, Tuple<T, State>> f)
{
Tuple<T, State> res;
while ((res = f(seed)) != null)
{
yield return res.Item1;
seed = res.Item2;
}
}
Run Code Online (Sandbox Code Playgroud)
f
将某个状态转换为元组.我们从元组返回第一个值并将我们的新状态设置为第二个值.这允许我们在整个计算过程中保持状态.
你这样使用它:
// return 0, 2, 3, 6, 8
var evens =
Unfold(0, state => state < 10 ? Tuple.Create(state, state + 2) : null)
.ToList();
// returns 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
var fibs =
Unfold(Tuple.Create(0, 1), state => Tuple.Create(state.Item1, Tuple.Create(state.Item2, state.Item1 + state.Item2)))
.Take(10).ToList();
Run Code Online (Sandbox Code Playgroud)
evens
相当简单,但fibs
更聪明一点.它state
实际上是一个元组,分别包含fib(n-2)和fib(n-1).
我不喜欢它们的滥用,因为它们产生的代码本身并不能解释,但它们很棒实现动态复合键,因为它们实现了IStructuralEquatable和IStructuralComparable(用于查找和排序)目的).
他们在内部组合了所有项目的哈希码; 例如,这里是Tuple的GetHashCode(取自ILSpy):
int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
{
return Tuple.CombineHashCodes(comparer.GetHashCode(this.m_Item1), comparer.GetHashCode(this.m_Item2), comparer.GetHashCode(this.m_Item3));
}
Run Code Online (Sandbox Code Playgroud)
元组非常适合一次执行多个异步IO操作并将所有值一起返回.以下是使用和不使用Tuple的示例.元组实际上可以使您的代码更清晰!
没有(讨厌的嵌套!):
Task.Factory.StartNew(() => data.RetrieveServerNames())
.ContinueWith(antecedent1 =>
{
if (!antecedent1.IsFaulted)
{
ServerNames = KeepExistingFilter(ServerNames, antecedent1.Result);
Task.Factory.StartNew(() => data.RetrieveLogNames())
.ContinueWith(antecedent2 =>
{
if (antecedent2.IsFaulted)
{
LogNames = KeepExistingFilter(LogNames, antecedent2.Result);
Task.Factory.StartNew(() => data.RetrieveEntryTypes())
.ContinueWith(antecedent3 =>
{
if (!antecedent3.IsFaulted)
{
EntryTypes = KeepExistingFilter(EntryTypes, antecedent3.Result);
}
});
}
});
}
});
Run Code Online (Sandbox Code Playgroud)
随着元组
Task.Factory.StartNew(() =>
{
List<string> serverNames = data.RetrieveServerNames();
List<string> logNames = data.RetrieveLogNames();
List<string> entryTypes = data.RetrieveEntryTypes();
return Tuple.Create(serverNames, logNames, entryTypes);
}).ContinueWith(antecedent =>
{
if (!antecedent.IsFaulted)
{
ServerNames = KeepExistingFilter(ServerNames, antecedent.Result.Item1);
LogNames = KeepExistingFilter(LogNames, antecedent.Result.Item2);
EntryTypes = KeepExistingFilter(EntryTypes, antecedent.Result.Item3);
}
});
Run Code Online (Sandbox Code Playgroud)
如果您正在使用有一个隐含的类型匿名函数反正那么你是不是让代码使用元组不太清楚.从方法中重新调整元组?在我的拙见中,当代码清晰度是关键时谨慎使用.我知道C#中的函数式编程难以抗拒,但我们必须考虑所有那些旧的笨重的"面向对象"的C#程序员.
我倾向于避免Tuple
大多数情况,因为它会损害可读性.但是,Tuple
在需要对不相关的数据进行分组时非常有用.
例如,假设您有一个汽车列表以及购买汽车的城市:
Mercedes, Seattle
Mustang, Denver
Mercedes, Seattle
Porsche, Seattle
Tesla, Seattle
Mercedes, Seattle
Run Code Online (Sandbox Code Playgroud)
您想要汇总每个城市每辆车的计数:
Mercedes, Seattle [3]
Mustang, Denver [1]
Porsche, Seattle [1]
Tesla, Seattle [1]
Run Code Online (Sandbox Code Playgroud)
为此,您创建一个Dictionary
.你有几个选择:
Dictionary<string, Dictionary<string, int>>
.Dictionary<CarAndCity, int>
.Dictionary<Tuple<string, string>, int>
.第一个选项丢失了可读性.它将要求您编写更多代码.
第二个选项有效且简洁,但是汽车和城市并没有真正相关,可能并不属于同一类.
第三种选择是简洁和干净.这是一个很好的用途Tuple
.
归档时间: |
|
查看次数: |
80305 次 |
最近记录: |