我爱tuples。它们允许您快速将相关信息分组在一起,而无需为其编写结构或类。这在重构非常本地化的代码时非常有用。
然而,初始化它们的列表似乎有点多余。
var tupleList = new List<Tuple<int, string>>
{
Tuple.Create( 1, "cow" ),
Tuple.Create( 5, "chickens" ),
Tuple.Create( 1, "airplane" )
};
有没有更好的方法?我希望找到Dictionary initializer的解决方案。
Dictionary<int, string> students = new Dictionary<int, string>()
{
{ 111, "bleh" },
{ 112, "bloeh" },
{ 113, "blah" }
};
我们不能使用类似的语法吗?
答案 0 :(得分:212)
是的! This is possible
集合初始值设定项的 {} 语法适用于任何 IEnumerable 具有添加方法的类型,其中包含正确数量的参数。 没有打扰如何在幕后工作,这意味着你可以 只需从 List&lt; T&gt; 扩展,添加一个自定义的Add方法来初始化你的 T ,你就完成了!
public class TupleList<T1, T2> : List<Tuple<T1, T2>>
{
public void Add( T1 item, T2 item2 )
{
Add( new Tuple<T1, T2>( item, item2 ) );
}
}
这允许您执行以下操作:
var groceryList = new TupleList<int, string>
{
{ 1, "kiwi" },
{ 5, "apples" },
{ 3, "potatoes" },
{ 1, "tomato" }
};
答案 1 :(得分:161)
c#7.0允许你这样做:
var tupleList = new List<(int, string)>
{
(1, "cow"),
(5, "chickens"),
(1, "airplane")
};
如果您不需要List
,只需要一个数组,则可以执行以下操作:
var tupleList = new(int, string)[]
{
(1, "cow"),
(5, "chickens"),
(1, "airplane")
};
如果你不喜欢“Item1”和“Item2”,你可以这样做:
var tupleList = new List<(int Index, string Name)>
{
(1, "cow"),
(5, "chickens"),
(1, "airplane")
};
或
var tupleList = new (int Index, string Name)[]
{
(1, "cow"),
(5, "chickens"),
(1, "airplane")
};
可让您执行:tupleList[0].Index
和tupleList[0].Name
框架4.6.2及以下
您必须从Nuget Package Manager安装System.ValueTuple
。
框架4.7及以上
它内置于框架中。 不安装System.ValueTuple
。实际上,将其删除并将其从bin目录中删除。
注意:在现实生活中,我无法在牛,鸡或飞机之间做出选择。我真的会被撕裂。
答案 2 :(得分:79)
C#6为此添加了一项新功能:扩展添加方法。对于VB.net来说这一直是可能的,但现在可以在C#中使用。
现在您不必直接向您的类添加Add()
方法,您可以将它们实现为扩展方法。使用Add()
方法扩展任何可枚举类型时,您将能够在集合初始值设定项表达式中使用它。因此,您不必再明确地从列表派生(as mentioned in another answer),您可以简单地扩展它。
public static class TupleListExtensions
{
public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
T1 item1, T2 item2)
{
list.Add(Tuple.Create(item1, item2));
}
public static void Add<T1, T2, T3>(this IList<Tuple<T1, T2, T3>> list,
T1 item1, T2 item2, T3 item3)
{
list.Add(Tuple.Create(item1, item2, item3));
}
// and so on...
}
这将允许您在任何实现IList<>
的类上执行此操作:
var numbers = new List<Tuple<int, string>>
{
{ 1, "one" },
{ 2, "two" },
{ 3, "three" },
{ 4, "four" },
{ 5, "five" },
};
var points = new ObservableCollection<Tuple<double, double, double>>
{
{ 0, 0, 0 },
{ 1, 2, 3 },
{ -4, -2, 42 },
};
当然,您不仅限于扩展元组集合,它可以用于您想要特殊语法的任何特定类型的集合。
public static class BigIntegerListExtensions
{
public static void Add(this IList<BigInteger> list,
params byte[] value)
{
list.Add(new BigInteger(value));
}
public static void Add(this IList<BigInteger> list,
string value)
{
list.Add(BigInteger.Parse(value));
}
}
var bigNumbers = new List<BigInteger>
{
new BigInteger(1), // constructor BigInteger(int)
2222222222L, // implicit operator BigInteger(long)
3333333333UL, // implicit operator BigInteger(ulong)
{ 4, 4, 4, 4, 4, 4, 4, 4 }, // extension Add(byte[])
"55555555555555555555555555555555555555", // extension Add(string)
};
C#7将添加对语言中内置元组的支持,但它们将采用不同的类型(System.ValueTuple
代替)。因此,为值元组添加重载会很好,因此您也可以选择使用它们。不幸的是,两者之间没有定义隐式转换。
public static class ValueTupleListExtensions
{
public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
ValueTuple<T1, T2> item) => list.Add(item.ToTuple());
}
这样列表初始化看起来会更好。
var points = new List<Tuple<int, int, int>>
{
(0, 0, 0),
(1, 2, 3),
(-1, 12, -73),
};
但是,不是经历所有这些麻烦,而是切换到仅使用ValueTuple
可能更好。
var points = new List<(int, int, int)>
{
(0, 0, 0),
(1, 2, 3),
(-1, 12, -73),
};
答案 3 :(得分:41)
你可以通过每次调用构造函数来做到这一点
var tupleList = new List<Tuple<int, string>>
{
new Tuple<int, string>(1, "cow" ),
new Tuple<int, string>( 5, "chickens" ),
new Tuple<int, string>( 1, "airplane" )
};
答案 4 :(得分:28)
老问题,但这是我通常做的事情,使事情更具可读性:
Func<int, string, Tuple<int, string>> tc = Tuple.Create;
var tupleList = new List<Tuple<int, string>>
{
tc( 1, "cow" ),
tc( 5, "chickens" ),
tc( 1, "airplane" )
};
答案 5 :(得分:1)
Super Duper老,我知道,但是我会在使用C#7的方法上添加有关使用Linq和延续lambda的文章。在类中重用时,我尝试使用命名元组代替DTO和匿名投影。是的,对于模拟和测试,您仍然需要类,但是内联和在类中进行传递很高兴拥有此更新的选项恕我直言。您可以从
实例化它们
- 直接实例化
var items = new List<(int Id, string Name)> { (1, "Me"), (2, "You")};
- 在现有集合的基础上,现在您可以返回类型正确的元组,类似于过去完成匿名投影的方式。
public class Hold
{
public int Id { get; set; }
public string Name { get; set; }
}
//In some method or main console app:
var holds = new List<Hold> { new Hold { Id = 1, Name = "Me" }, new Hold { Id = 2, Name = "You" } };
var anonymousProjections = holds.Select(x => new { SomeNewId = x.Id, SomeNewName = x.Name });
var namedTuples = holds.Select(x => (TupleId: x.Id, TupleName: x.Name));
- 稍后使用分组方法重用元组,或使用一种方法以其他逻辑内联地构造它们:
//Assuming holder class above making 'holds' object
public (int Id, string Name) ReturnNamedTuple(int id, string name) => (id, name);
public static List<(int Id, string Name)> ReturnNamedTuplesFromHolder(List<Hold> holds) => holds.Select(x => (x.Id, x.Name)).ToList();
public static void DoSomethingWithNamedTuplesInput(List<(int id, string name)> inputs) => inputs.ForEach(x => Console.WriteLine($"Doing work with {x.id} for {x.name}"));
var namedTuples2 = holds.Select(x => ReturnNamedTuple(x.Id, x.Name));
var namedTuples3 = ReturnNamedTuplesFromHolder(holds);
DoSomethingWithNamedTuplesInput(namedTuples.ToList());
答案 6 :(得分:0)
我认为一种技术更容易,而且之前没有提及过:
var asdf = new [] {
(Age: 1, Name: "cow"),
(Age: 2, Name: "bird")
}.ToList();
我觉得它比以前更清洁了。
var asdf = new List<Tuple<int, string>> {
(Age: 1, Name: "cow"),
(Age: 2, Name: "bird")
};
答案 7 :(得分:-1)
为什么喜欢元组?它就像匿名类型:没有名字。无法理解数据结构。
我喜欢经典课程
class FoodItem
{
public int Position { get; set; }
public string Name { get; set; }
}
List<FoodItem> list = new List<FoodItem>
{
new FoodItem { Position = 1, Name = "apple" },
new FoodItem { Position = 2, Name = "kiwi" }
};
答案 8 :(得分:-5)
var colors = new[]
{
new { value = Color.White, name = "White" },
new { value = Color.Silver, name = "Silver" },
new { value = Color.Gray, name = "Gray" },
new { value = Color.Black, name = "Black" },
new { value = Color.Red, name = "Red" },
new { value = Color.Maroon, name = "Maroon" },
new { value = Color.Yellow, name = "Yellow" },
new { value = Color.Olive, name = "Olive" },
new { value = Color.Lime, name = "Lime" },
new { value = Color.Green, name = "Green" },
new { value = Color.Aqua, name = "Aqua" },
new { value = Color.Teal, name = "Teal" },
new { value = Color.Blue, name = "Blue" },
new { value = Color.Navy, name = "Navy" },
new { value = Color.Pink, name = "Pink" },
new { value = Color.Fuchsia, name = "Fuchsia" },
new { value = Color.Purple, name = "Purple" }
};
foreach (var color in colors)
{
stackLayout.Children.Add(
new Label
{
Text = color.name,
TextColor = color.value,
});
FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label))
}
this is a Tuple<Color, string>