[c#] 사전에 AddRange가없는 이유는 무엇입니까?

제목은 충분히 기본적입니다. 내가 할 수없는 이유 :

Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());



답변

원래 질문에 대한 의견은 이것을 아주 잘 요약합니다.

아무도 그 기능을 설계, 지정, 구현, 테스트, 문서화 및 제공하지 않았기 때문입니다. -@Gabe Moothart

그 이유는? 글쎄요, 딕셔너리 병합의 동작이 프레임 워크 지침에 맞는 방식으로 추론 될 수 없기 때문일 것입니다.

AddRange데이터 범위가 중복 항목을 허용하므로 범위가 연관 컨테이너에 의미가 없기 때문에 존재하지 않습니다. 예를 들어 IEnumerable<KeyValuePair<K,T>>해당 컬렉션 이있는 경우 중복 항목을 방지하지 않습니다.

키-값 쌍 모음을 추가하거나 두 사전을 병합하는 동작은 간단합니다. 그러나 여러 중복 항목을 처리하는 방법은 그렇지 않습니다.

중복을 처리 할 때 메서드의 동작은 어떻게됩니까?

내가 생각할 수있는 적어도 세 가지 해결책이 있습니다.

  1. 중복 된 첫 번째 항목에 대해 예외를 발생시킵니다.
  2. 모든 중복 항목을 포함하는 예외 발생
  3. 중복 무시

예외가 발생하면 원본 사전의 상태는 어떻습니까?

Add거의 항상 원자 적 작업으로 구현됩니다. 성공하고 컬렉션의 상태를 업데이트하거나 실패하고 컬렉션의 상태는 변경되지 않은 상태로 유지됩니다. 으로 AddRange복제 오류로 인해 실패 할 수 있습니다, 방법은 일관성있는 동작을 유지 Add또한 중복에 예외를 throw하여 원자 확인하고 변경으로 원래 사전의 상태를두고하는 것입니다.

API 소비자로서 중복 요소를 반복적으로 제거해야하는 것은 지루할 것입니다. 이는 모든 중복 값 AddRange을 포함하는 단일 예외를 throw해야 함을 의미 합니다.

선택은 다음과 같이 요약됩니다.

  1. 모든 중복 항목과 함께 예외를 throw하고 원본 사전은 그대로 둡니다.
  2. 중복을 무시하고 계속하십시오.

두 가지 사용 사례를 모두 지원하는 주장이 있습니다. 이를 위해 IgnoreDuplicates서명에 플래그를 추가 합니까?

IgnoreDuplicates(true로 설정) 플래그는 기본이되는 구현으로, 상당한 속도를 제공 할 것 중복 검사에 대한 코드 우회.

이제 AddRange두 경우를 모두 지원할 수 있지만 문서화되지 않은 부작용 이있는 플래그가 있습니다 (프레임 워크 디자이너가 피하기 위해 정말 열심히 노력한 것입니다).

요약

중복을 처리 할 때 명확하고 일관 적이며 예상되는 동작이 없기 때문에 모두 함께 처리하지 않고 시작할 방법을 제공하지 않는 것이 더 쉽습니다.

계속해서 사전을 병합해야하는 경우, 물론 응용 프로그램에 적합한 방식으로 작동하는 사전을 병합하는 자체 확장 메서드를 작성할 수 있습니다.


답변

몇 가지 해결책이 있습니다.

Dictionary<string, string> mainDic = new Dictionary<string, string>() {
    { "Key1", "Value1" },
    { "Key2", "Value2.1" },
};
Dictionary<string, string> additionalDic= new Dictionary<string, string>() {
    { "Key2", "Value2.2" },
    { "Key3", "Value3" },
};
mainDic.AddRangeOverride(additionalDic); // Overrides all existing keys
// or
mainDic.AddRangeNewOnly(additionalDic); // Adds new keys only
// or
mainDic.AddRange(additionalDic); // Throws an error if keys already exist
// or
if (!mainDic.ContainsKeys(additionalDic.Keys)) // Checks if keys don't exist
{
    mainDic.AddRange(additionalDic);
}

namespace MyProject.Helper
{
  public static class CollectionHelper
  {
    public static void AddRangeOverride<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
    {
        dicToAdd.ForEach(x => dic[x.Key] = x.Value);
    }

    public static void AddRangeNewOnly<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
    {
        dicToAdd.ForEach(x => { if (!dic.ContainsKey(x.Key)) dic.Add(x.Key, x.Value); });
    }

    public static void AddRange<TKey, TValue>(this IDictionary<TKey, TValue> dic, IDictionary<TKey, TValue> dicToAdd)
    {
        dicToAdd.ForEach(x => dic.Add(x.Key, x.Value));
    }

    public static bool ContainsKeys<TKey, TValue>(this IDictionary<TKey, TValue> dic, IEnumerable<TKey> keys)
    {
        bool result = false;
        keys.ForEachOrBreak((x) => { result = dic.ContainsKey(x); return result; });
        return result;
    }

    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var item in source)
            action(item);
    }

    public static void ForEachOrBreak<T>(this IEnumerable<T> source, Func<T, bool> func)
    {
        foreach (var item in source)
        {
            bool result = func(item);
            if (result) break;
        }
    }
  }
}

즐기세요.


답변

누군가가 나처럼이 질문을 접하게되면 IEnumerable 확장 메서드를 사용하여 “AddRange”를 달성 할 수 있습니다.

var combined =
    dict1.Union(dict2)
        .GroupBy(kvp => kvp.Key)
        .Select(grp => grp.First())
        .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

사전을 결합 할 때 주요 트릭은 중복 키를 처리하는 것입니다. 위의 코드에서는 부분 .Select(grp => grp.First())입니다. 이 경우 단순히 중복 그룹에서 첫 번째 요소를 가져 오지만 필요한 경우 더 정교한 논리를 구현할 수 있습니다.


답변

내 생각 엔 무슨 일이 일어 났는지에 대해 사용자에게 적절한 출력이 부족한 것 같습니다. 사전에 반복 키를 가질 수 없기 때문에 일부 키가 교차하는 두 사전 병합을 어떻게 처리할까요? 물론 “I do n’t care”라고 말할 수 있지만 이는 false를 반환하거나 반복되는 키에 대한 예외를 던지는 규칙을 위반하는 것입니다.


답변

당신은 이것을 할 수 있습니다

Dictionary<string, string> dic = new Dictionary<string, string>();
// dictionary other items already added.
MethodThatReturnAnotherDic(dic);

public void MethodThatReturnAnotherDic(Dictionary<string, string> dic)
{
    dic.Add(.., ..);
}

또는 addrange 및 / 또는 위의 패턴을 사용하는 목록을 사용합니다.

List<KeyValuePair<string, string>>


답변

새 딕셔너리를 처리하는 경우 (그리고 잃을 기존 행이없는 경우) 항상 다른 객체 목록에서 ToDictionary ()를 사용할 수 있습니다.

따라서 귀하의 경우 다음과 같이 할 수 있습니다.

Dictionary<string, string> dic = new Dictionary<string, string>();
dic = SomeList.ToDictionary(x => x.Attribute1, x => x.Attribute2);


답변

중복 키가 없을 것임을 알고 있다면 다음을 수행 할 수 있습니다.

dic = dic.Union(MethodThatReturnAnotherDic()).ToDictionary(kvp => kvp.Key, kvp => kvp.Value);

중복 키 / 값 쌍이있는 경우 예외가 발생합니다.

왜 이것이 프레임 워크에 없는지 모르겠습니다. 해야한다. 불확실성은 없습니다. 예외를 던지십시오. 이 코드의 경우 예외가 발생합니다.