2015-01-27 1 views
0

Хорошо, я знаю, что, возможно, я заблудился в сфере чрезмерного поведения, но мне все же хотелось бы знать, как сделать следующую работу. Я собираюсь переработать фактическое приложение, чтобы не идти на такие длины, но для будущих ссылок это кажется хорошим вопросом.Объединить два объекта, которые, как я знаю, имеют один и тот же IEnumerable <> type

Ситуация:

У меня есть ExpandoObject, к которому я добавляю свойства, string paramName. Все это отлично поработало, но потом я понял, что иногда я хочу иметь свойство ExpandoObject, которое является каким-то образом IEnumerable<>, и когда я устанавливаю параметр для второго и последующих времен, я не хочу изменять сохраненный список, но я хочу чтобы объединить его.

Проблема, конечно, что IEnumerable не имеет Add способ. Итак, я могу просто заменить конкатенацией двух IEnumerable<> того же типа. Я могу пожертвовать способностью использовать не-общие IEnumerables.

я чувствовал себя очень изобретательный, пока IntelliSense не запретил мне писать eo[paramName] = (eo[paramName] as IEnumerable<>).Concat(param as IEnumerable<>); Как вы можете видеть полный код, я знаю точно, что оба param и eo[paramName] являются своего рода IEnumerable<>, но я не знаю, как сказать, компилятор что я хочу, чтобы он сделал.

Любые предложения?

private void SetParam<T>(IDictionary<string, object> eo, string paramName, T param){ 
    // eo is (ExpandoObject as IDictionary<string, object>) 
    var enumerableType = GetEnumerableType(typeof (T)); 
     if (enumerableType == null) 
     { 
      // is not IEnumerable<>, but it might be not-supported IEnumerable 
      if (typeof (T).GetInterfaces().Contains(typeof (System.Collections.IEnumerable))) 
       throw new NotSupportedException("Non-generic collection types are not supported"); 

      // it is just plain not-colection, so set value 
      eo[paramName] = param; 
     } 
     else 
     { 
      // first ensure that there is at least empty collection 
      if (eo[paramName] == null) 
       eo[paramName] = Activator.CreateInstance<T>(); 

      // and concatenate 
      eo[paramName] = (eo[paramName] as IEnumerable<>).Concat(param as IEnumerable<>); 
      // or whatever other way to add param to eo[paramName] 
     } 
    } 
    // from: http://stackoverflow.com/a/1846690/1417905 
    private static Type GetEnumerableType(Type type) 
    { 
     return (from intType in type.GetInterfaces() 
      where intType.IsGenericType && intType.GetGenericTypeDefinition() == typeof (IEnumerable<>) 
      select intType.GetGenericArguments()[0]).FirstOrDefault(); 
    } 
+0

Я не понимаю. У вас есть класс с именем ExpandoObject? Или вы просто хотите заполнить словарь? – EagleBeak

+0

@EagleBeak ExpandoObject - это динамический класс Microsoft: https://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject%28v=vs.110%29.aspx :) – Gerino

+0

Как насчет использования Linq: 'var result = ((IEnumerable) prm) .OfType () .Union ((IEnumerable) prm2) .OfType <объект()).OfType () ' –

ответ

-1

Вы должны указать параметр типа для IEnumerable<T>. И вам не разрешено конкатцировать два перечисления другого типа. Например, это недопустимо: Как упоминалось в комментариях Servy, разрешено конкатцировать два перечисления другого типа. Даже если они явно не используют базовый тип, поскольку каждый ссылочный тип в .NET имеет тип object. Так разрешается:

IEnumerable<Apple> apples = new List<Apple>(); 
IEnumerable<Car> cars = new List<Car>(); 

cars.Concat(apples); // compiler error 
IEnumerable<object> enuerableOfObjets = cars.Concat<object>(apples); 

Но, на мой взгляд, это приводит всю концепцию родового коллекции абсурду.

Вы можете сделать почти то, что вы хотите, разделив метод двумя способами:

private void SetParam<T>(IDictionary<string, object> eo, string paramName, T param) 
    { 
     // is not IEnumerable<>, but it might be not-supported IEnumerable 
     if (typeof(T).GetInterfaces().Contains(typeof(System.Collections.IEnumerable))) 
      throw new NotSupportedException("Non-generic collection types are not supported"); 

     // it is just plain not-colection, so set value 
     eo[paramName] = param; 
    } 

    private void SetParam<T>(IDictionary<string, object> eo, string paramName, IEnumerable<T> param) 
    { 
     if (!eo.ContainsKey(paramName)) 
      eo[paramName] = param; 
     else 
      eo[paramName] = (eo[paramName] as IEnumerable<T>).Concat(param); 
    } 

Это решение concats IEnumerable<T> «S с тем же типом T. Если существующий перечислимый тип отличается от того, который вы пытаетесь добавить, это приведет к ошибке выполнения.

+0

Я буду тестировать его позже - я думаю, что мне может понадобиться фактически не использовать перегрузку' SetParam', но выполнить ручную проверку и вызывать либо «SetParam», либо «SetEnumerableParam», что касается времени выполнения обеих перегрузок будет соответствовать, когда вызывается: 'U myParam; SetParam (eo, paramName, myParam); ' – Gerino

+0

Также - никоим образом этот метод не может вызывать для' IEnumerable 'и' IEnumerable ', используя тот же paramName, другая часть кода гарантирует это для меня :) – Gerino

+0

Я не знаю, t получить то, что вы подразумеваете под «как для времени выполнения, так и для перегрузок будет соответствовать» –

1

Вы можете заставить его использовать отражение. Я объявил метод, как это:

public IEnumerable<T> Concat<T>(IEnumerable<T> obj, IEnumerable<T> obj2) 
{ 
    return obj.Concat(obj2); 
} 

И тогда вместо линии eo[paramName] = (eo[paramName] as IEnumerable<>).Concat(param as IEnumerable<>);

вставить этот код:

MethodInfo info = GetType().GetMethod("Concat"); 
info = info.MakeGenericMethod(enumerableType); 
eo[paramName] = info.Invoke(this, new [] {eo[paramName], param});