2012-06-24 16 views
20

Tại sao là nó có thể khởi tạo một Dictionary<T1,T2> như thế này:Cách tạo công việc khởi tạo mảng nội tuyến như ví dụ: Khởi tạo từ điển?

var dict = new Dictionary<string,int>() { 
    { "key1", 1 }, 
    { "key2", 2 } 
}; 

... nhưng không thể khởi tạo, chẳng hạn, một mảng của các đối tượng trong KeyValuePair<T1,T2> chính xác theo cùng một cách:

var kvps = new KeyValuePair<string,int>[] { 
    { "key1", 1 }, 
    { "key2", 2 } 
}; 
// compiler error: "Array initializers can only be used in a variable 
// or field initializer. Try using a new expression instead." 

Tôi nhận ra tôi có thể làm ví dụ thứ hai bằng cách chỉ viết new KeyValuePair<string,int>() { "key1", 1 }, v.v. cho mỗi mục. Nhưng tôi tự hỏi nếu nó có thể sử dụng cùng một loại cú pháp ngắn gọn có thể trong ví dụ đầu tiên.

Nếu không thể, điều gì làm cho loại từ điển trở nên đặc biệt?

+0

Tôi thích câu hỏi này sẽ diễn ra ở đâu, nhưng có vẻ như chưa tinh chỉnh. Tôi thích ý tưởng tập trung vào 'List' so với' Dictionary' (để loại bỏ mảng Array), mặc dù thông báo lỗi có thể khác? (Musing: Làm thế nào để biết làm thế nào để tạo ra một KVP từ '{a, b}'? ;-) –

+0

(Tôi không nghĩ rằng tôi đã nhận được danh hiệu khá đúng, mặc dù.) –

+0

@pst có, tôi nghĩ rằng bạn ' trở nên gần gũi hơn với cốt lõi của những gì tôi đang yêu cầu. Tôi không giỏi viết câu hỏi ... – McGarnagle

Trả lời

33

Bộ sưu tập initializer cú pháp được dịch sang các cuộc gọi đến Add với số lượng thích hợp các thông số:

var dict = new Dictionary<string,int>(); 
dict.Add("key1", 1); 
dict.Add("key2", 2); 

cú pháp khởi tạo đặc biệt này cũng sẽ làm việc trên các lớp khác mà có một phương pháp Add và thực hiện IEnumerable. Hãy tạo ra một lớp hoàn toàn điên chỉ để chứng minh rằng không có gì đặc biệt về Dictionary là và cú pháp này có thể làm việc cho bất kỳ lớp học phù hợp:

// Don't do this in production code! 
class CrazyAdd : IEnumerable 
{ 
    public void Add(int x, int y, int z) 
    { 
     Console.WriteLine(x + y + z); // Well it *does* add... 
    } 

    public IEnumerator GetEnumerator() { throw new NotImplementedException(); } 
} 

Bây giờ bạn có thể viết này:

var crazyAdd = new CrazyAdd 
{ 
    {1, 2, 3}, 
    {4, 5, 6} 
}; 

Đầu ra:

6 
15 

Xem hoạt động trực tuyến: ideone

Đối với các loại khác mà bạn đã hỏi về:

  • Nó không hoạt động trên mảng vì nó không có phương pháp Add.
  • List<T> có phương thức Add nhưng chỉ có một tham số.
+1

Ok ...nhưng * Danh sách * ** không ** có phương thức thêm và cố gắng tạo ra cùng một lỗi. – McGarnagle

+0

Xem cập nhật để được giải thích. –

+1

đã tự hỏi về điều này mãi mãi, nhờ –

3

Sự cố của bạn bắt nguồn từ thực tế rằng đó là một mảng chứ không phải bộ sưu tập.

var kvps = new KeyValuePair<string,int>[] { 
    { "key1", 1 }, 
    { "key2", 2 } 
}; 

thực sự cần là:

var kvps = new KeyValuePair<string, int>[] { 
    new KeyValuePair<string, int>("key1", 1), 
    new KeyValuePair<string, int>("key2", 2) 
}; 

Các giveaway là dấu ngoặc đơn. [] là một mảng. {} là một bộ sưu tập.

+4

Có thể câu hỏi của tôi không được thể hiện rõ ràng, nhưng hãy đọc kỹ hơn. Tôi đã đề cập ví dụ của bạn trong câu trả lời của tôi. Tôi biết làm thế nào để viết khởi tạo, nhưng tôi tự hỏi về lý do cơ bản cú pháp đơn giản là không thể. – McGarnagle

11

Nó hoạt động với từ điển, vì nó có quá tải cho Add có hai đối số. Mảng thậm chí không có phương thức Add, hãy để một mình với hai đối số.

Lớp Dictionary được thiết kế đặc biệt để làm việc với KeyValuePair<,> nội bộ, đó là lý do duy nhất bạn không cần phải gọi các nhà xây dựng bằng tay, thay vì hai đối số Add được gọi và xây dựng KeyValuePair dưới mui xe.

Mỗi khác IEnumerable<KeyValuePair<,>> không có thực hiện đặc biệt này và do đó phải được khởi tạo theo cách này:

var array = new KeyValuePair<int, int>[] { 
    new KeyValuePair<int, int>(1, 2), 
    new KeyValuePair<int, int>(3, 4) 
}; 

Bạn có thể tạo ra các hành vi tương tự với các lớp học của riêng mình, như cho phép nói rằng bạn thực hiện điều này:

class ThreeTupleList<T1, T2, T3> : List<Tuple<T1, T2, T3>> 
{ 
    public void Add(T1 a, T2 b, T3 c) 
    { 
     this.Add(new Tuple<T1, T2, T3>(a, b, c)); 
    } 

    // You can even implement a two-argument Add and mix initializers 
    public void Add(T1 a, T2 b) 
    { 
     this.Add(new Tuple<T1, T2, T3>(a, b, default(T3))); 
    } 
} 

bạn có thể khởi tạo nó như thế này, và thậm chí trộn ba, hai và một đối số initializers:

var mylist = new ThreeTupleList<int, string, double>() 
{ 
    { 1, "foo", 2.3 }, 
    { 4, "bar", 5.6 }, 
    { 7, "no double here" }, 
    null 
}; 
0

Bạn có thể không thấy nhưng có cùng cú pháp. Sự khác biệt duy nhất là một mảng lấy các phần tử làm đầu vào trong đó một từ điển lấy một mảng 2 chiều.

int[] a = new int[]{5,6}; 
int[,] a = new int[]{{5,6},{3,6}}; 
Dictionary<int,int> a = new Dictionary<int,int>{{5,6},{3,6}}; 
+3

Đó không phải chính xác lý do tại sao đường cú pháp này là có thể. Trên thực tế nó là do việc thực hiện từ điển chung, cung cấp một đối số hai '' Add'' mà xây dựng '' KeyValuePair'' cho bạn dưới mui xe. –

2

Nhờ nhiều người trả lời để chỉ ra rằng phương pháp Thêm điều huyền diệubí mật sốt mà làm cho công việc Cú pháp khởi tạo. Vì vậy, tôi có thể đạt được mục tiêu của mình bằng cách kế thừa lớp trong câu hỏi (KeyValuePair):

public class InitializableKVPs<T1,T2> : IEnumerable<KeyValuePair<T1,T2>> 
{ 
    public void Add(T1 key, T2 value) 
    { 
     throw new NotImplementedException(); 
    } 

    public IEnumerator<KeyValuePair<string,string>> GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
} 

này bây giờ được chấp nhận bởi trình biên dịch:

var kvps = new InitializableKVPs<string,int> { 
    { "key1", 1 }, 
    { "key2", 2 } 
}; 

Sửa: Câu trả lời Philip Daubmeier của có một thực tế, súc tích thực hiện điều này.

+2

Chính xác. Xem câu trả lời được cập nhật của tôi để thực hiện rất đơn giản điều này, nếu bạn chỉ kế thừa từ một "ICollection <>' 'đã được triển khai, ví dụ: '' List <> '' –

+0

Chỉ cần phát hiện ra bạn thậm chí có thể trộn các phần tử khởi tạo với số lượng đối số khác nhau :) Cập nhật câu trả lời của tôi một lần nữa ... –

1

Override IDictionary cho C# 6.0 cú pháp

Trong trường hợp ai đó đến đây, như tôi đã làm, tìm cách để tiết kiệm một số tổ hợp phím cho C# 6.0 từ điển initializer cú pháp mới, nó có thể được thực hiện, nhưng đòi hỏi phải phát sinh từ IDictionary thay . Bạn chỉ cần triển khai phương thức thiết lập [] này để làm việc này, để lại một tấn các phương thức không được thực hiện.

Việc thực hiện lớp trông như thế này:

// Seriously! Don't do this in production code! Ever!!! 
public class CrazyAdd2 : IDictionary<string, int> 
{ 
    public int this[string key] 
    { 
     get { throw new NotImplementedException(); } 
     set {Console.WriteLine($"([{key}]={value})"); } 
    } 

#region NotImplemented 
// lots of empty methods go here 
#endregion 
} 

sau đó sử dụng nó:

var crazyAdd2 = new CrazyAdd2 
{ 
    ["one"] = 1, 
    ["two"] = 2, 
}; 

và đầu ra:

([one]=1) 
([two]=2) 

Và đây là một fiddle chứng minh toàn bộ sự việc:

https://dotnetfiddle.net/Lovy7m