在我看来,元组是编写结果类的捷径(我敢肯定,还有其他用途)。
实际上,还有其他有价值的用途Tuple<>
-其中大多数涉及抽象化共享相似结构的特定类型组的语义,并将它们简单地视为有序的值集。在所有情况下,元组的好处是它们避免使用仅公开数据类的属性(而不是方法)使名称空间混乱。
这是合理使用的示例Tuple<>
:
var opponents = new Tuple<Player,Player>( playerBob, playerSam );
在上面的示例中,我们要代表一对对手,元组是将这些实例配对的一种便捷方法,而无需创建新类。这是另一个例子:
var pokerHand = Tuple.Create( card1, card2, card3, card4, card5 );
扑克手可以看作只是一组纸牌-元组(可能是)表达该概念的一种合理方法。
抛开我遗漏Tuples的可能性,Tuple的示例是否是错误的设计选择?
将强类型Tuple<>
实例作为公共类型的公共API的一部分返回并不是一个好主意。如您所知,元组要求相关各方(图书馆作者,图书馆用户)提前就所使用的元组类型的目的和解释达成协议。创建直观而清晰的API充满挑战,Tuple<>
仅公开使用会掩盖API的意图和行为。
匿名类型也是元组的一种 -但是,它们是强类型的,并且允许您为属于该类型的属性指定清晰,有用的名称。但是匿名类型很难跨不同的方法使用-匿名类型主要是为支持LINQ等技术而添加的,其中投影会产生我们通常不希望分配名称的类型。(是的,我知道编译器会合并具有相同类型和命名属性的匿名类型)。
我的经验法则是: 如果要从公共接口返回它,请将其命名为type。
使用元组的另一个经验法则是: 尽可能清楚地命名方法参数和类型的localc变量Tuple<>
-使名称代表元组元素之间关系的含义。想想我的var opponents = ...
例子。
这是一个现实情况的示例,在该示例中,我曾经Tuple<>
避免声明仅在自己的程序集中使用的仅数据类型。这种情况涉及这样一个事实,当使用包含匿名类型的通用字典时,很难使用该TryGetValue()
方法在字典中查找项目,因为该方法需要一个out
无法命名的参数:
public static class DictionaryExt
{
// helper method that allows compiler to provide type inference
// when attempting to locate optionally existent items in a dictionary
public static Tuple<TValue,bool> Find<TKey,TValue>(
this IDictionary<TKey,TValue> dict, TKey keyToFind )
{
TValue foundValue = default(TValue);
bool wasFound = dict.TryGetValue( keyToFind, out foundValue );
return Tuple.Create( foundValue, wasFound );
}
}
public class Program
{
public static void Main()
{
var people = new[] { new { LastName = "Smith", FirstName = "Joe" },
new { LastName = "Sanders", FirstName = "Bob" } };
var peopleDict = people.ToDictionary( d => d.LastName );
// ??? foundItem <= what type would you put here?
// peopleDict.TryGetValue( "Smith", out ??? );
// so instead, we use our Find() extension:
var result = peopleDict.Find( "Smith" );
if( result.First )
{
Console.WriteLine( result.Second );
}
}
}
PS还有另一种(更简单的)方法可以解决字典中由匿名类型引起的问题,那就是使用var
关键字让编译器为您“推断”类型。这是该版本:
var foundItem = peopleDict.FirstOrDefault().Value;
if( peopleDict.TryGetValue( "Smith", out foundItem ) )
{
// use foundItem...
}