Подтвердить что ты не робот

Могу ли я создать словарь общих типов?

Я хотел бы создать объект Dictionary с строковыми ключами, удерживая значения, которые имеют общий тип. Я предполагаю, что это будет выглядеть примерно так:

Dictionary<string, List<T>> d = new Dictionary<string, List<T>>();  

И позвольте мне добавить следующее:

d.Add("Numbers", new List<int>());
d.Add("Letters", new List<string>());

Я знаю, что могу сделать это для списка строк, например, используя этот синтаксис:

Dictionary<string, List<string>> d = new Dictionary<string, List<string>>();
d.Add("Key", new List<string>());

но я хотел бы сделать это для общего списка, если это возможно...

2 вопроса:

  • Возможно ли это?
  • Какой синтаксис?

Большое спасибо,
Джон

4b9b3361

Ответ 1

EDIT: Теперь я перечитал вопрос...

Вы не можете этого сделать, но пользовательская коллекция будет обрабатывать ее в некоторой степени. Обычно у вас есть общий метод Add:

public void Add<T>(string key, List<T> list)

(Сама коллекция не будет общей), если вы не хотите создать общий тип ключа.)

Вы не могли извлечь из него значения строго типизированным образом, потому что компилятор не знает, какой тип вы использовали для определенного ключа. Если вы сделаете ключ сам по себе, вы закончите с немного лучшей ситуацией, но тот, который по-прежнему не поддерживается существующими коллекциями. Эта ситуация отвечала моим первоначальным ответам.

EDIT: оригинальный ответ, когда я не совсем правильно прочитал вопрос, но который может быть информативным в любом случае...

Нет, я не могу заставить один аргумент типа зависеть от другого. Это всего лишь одна из вещей, которые можно было бы выразить в системе общего типа, но какие ограничения .NET не позволяют. Такие проблемы всегда будут возникать, и разработчики .NET предпочли сохранить простые методы относительно простых.

Однако вы можете написать коллекцию, чтобы обеспечить ее соблюдение довольно легко. У меня пример в сообщении в блоге, который сохраняет только одно значение, но было бы легко расширить его, чтобы использовать список.

Ответ 2

Что-то вроде этой работы?

public class GenericDictionary
{
    private Dictionary<string, object> _dict = new Dictionary<string, object>();

    public void Add<T>(string key, T value) where T : class
    {
        _dict.Add(key, value);
    }

    public T GetValue<T>(string key) where T : class
    {
        return _dict[key] as T;
    }
}

В принципе, он заворачивает все кастинг за кулисами для вас.

Ответ 3

Я предпочитаю этот способ размещения родовых типов в коллекцию:

interface IList
{
  void Add (object item);
}

class MyList<T> : List<T>, IList
{
  public void Add (object item)
  {
    base.Add ((T) item); // could put a type check here
  }
}

class Program
{
  static void Main (string [] args)
  {
    SortedDictionary<int, IList>
      dict = new SortedDictionary<int, IList> ();

    dict [0] = new MyList<int> ();
    dict [1] = new MyList<float> ();

    dict [0].Add (42);
    dict [1].Add ("Hello"); // Fails! Type cast exception.
  }
}

Но вы теряете проверки типов во время компиляции.

Ответ 4

Мы используем много отражений для создания расширяемого инструмента администрирования. Нам понадобился способ зарегистрировать элементы в глобальном поиске в определении модуля. Каждый поиск возвращал результаты согласованным образом, но каждый из них имел разные зависимости. Вот пример того, что мы регистрируем поиск для одного модуля:

public void ConfigureSearch(ISearchConfiguration config)
    {
        config.AddGlobalSearchCallback<IEmploymentDataContext>((query, ctx) =>
        {
            return ctx.Positions.Where(p => p.Name.Contains(query)).ToList().Select(p =>
                new SearchResult("Positions", p.Name, p.ThumbnailUrl,
                    new UrlContext("edit", "position", new RouteValueDictionary(new { Id = p.Id }))
                    ));
        });
    }

В фоновом режиме во время регистрации модуля мы перебираем каждый модуль и добавляем Func к поисковому счету с экземпляром:

public class GenericFuncCollection : IEnumerable<Tuple<Type, Type, Object>>
{
    private List<Tuple<Type, Type, Object>> objects = new List<Tuple<Type, Type, Object>>();

    /// <summary>
    /// Stores a list of Func of T where T is unknown at compile time.
    /// </summary>
    /// <typeparam name="T1">Type of T</typeparam>
    /// <typeparam name="T2">Type of the Func</typeparam>
    /// <param name="func">Instance of the Func</param>
    public void Add<T1, T2>(Object func)
    {
        objects.Add(new Tuple<Type, Type, Object>(typeof(T1), typeof(T2), func));
    }

    public IEnumerator<Tuple<Type, Type, object>> GetEnumerator()
    {
        return objects.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return objects.GetEnumerator();
    }
}

Тогда, когда мы, наконец, назовем это, мы делаем это с отражением:

var dependency = DependencyResolver.Current.GetService(search.Item1);
var methodInfo = search.Item2.GetMethod("Invoke");
return (IEnumerable<SearchResult>)methodInfo.Invoke(search.Item3, new Object[] { query, dependency });

Ответ 5

Я не нашел то, что искал здесь, но после прочтения я думаю, что это может быть то, о чем просят, чтобы попытаться ответить.

Проблема заключается в том, что при использовании словаря это замкнутый построенный тип, и все элементы должны быть типа TValue. Я вижу этот вопрос в нескольких местах без хорошего ответа.

Факт в том, что я хочу индексировать, но каждый элемент имеет другой тип и на основе значения TKey мы уже знаем тип. Не пытайтесь обойти бокс, но пытаться просто получить более элегантный доступ к чему-то вроде DataSetExtensions Field. И не хотите использовать динамическое, потому что типы известны и просто не нужны.

Решением может быть создание не общего типа, который не выставляет T на уровне класса и, следовательно, вызывает закрытие части TValue словаря. Затем посыпьте свободным методом, чтобы помочь инициализировать.

public class GenericObject
{
    private object value;

    public T GetValue<T>()
    {
        return (T)value;
    }

    public void SetValue<T>(T value)
    {
        this.value = value;
    }

    public GenericObject WithValue<T>(T value)
    {
        this.value = value;
        return this;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, GenericObject> dict = new Dictionary<string, GenericObject>();

        dict["mystring"] = new GenericObject().WithValue<string>("Hello World");
        dict["myint"] = new GenericObject().WithValue<int>(1);

        int i = dict["myint"].GetValue<int>();
        string s = dict["mystring"].GetValue<string>();
    }
}

Ответ 6

Как насчет Dictionary<string, dynamic>? (при условии, что вы находитесь на С# 4)

Dictionary<string, dynamic> Dict = new Dictionary<string, dynamic>();

Источник: fooobar.com/questions/148827/...

Ответ 7

Другая возможность использовать переменную dynamic.

Например:

Dictionary<string, List<dynamic>> d = new Dictionary<string, List<dynamic>>(); d.Add("Key", new List<dynamic>());

переменная динамическая разрешает тип во время выполнения.

Ответ 8

Один из способов - создать значение словаря с типом "объект", например:

Dictionary<string, object> d = new Dictionary<string, object>();

Итак, здесь тип данных объекта используется как общий тип данных, вы можете поместить что-либо в это как значение.

Ответ 9

Нет, но вы можете использовать объект вместо универсального типа.

Длинный ответ: Текущая версия С# не позволит вам делать записи универсального типа в словаре. Возможны следующие варианты: а) создать пользовательский класс, который совпадает со словарем, за исключением того, что позволяет ему принимать универсальные типы, или б) заставить ваш Словарь принимать значения типа объекта. Я считаю вариант б более простым подходом.

Если вы отправляете списки определенных типов, то при обработке списков вам придется проверить, что это за список. Лучшим подходом является создание списков объектов; таким образом, вы можете вводить целые числа, строки или любой другой тип данных, который вам нужен, и вам не обязательно проверять, какой тип объекта содержит список. Это (предположительно) произведет эффект, который вы ищете.

Вот короткая консольная программа, которая делает свое дело:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace dictionary
{
class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, object> dic = new Dictionary<string, object>();
        var lstIntList = new List<object>();
        var lstStrings = new List<object>();
        var lstObjects = new List<object>();
        string s = "";

        lstIntList.Add(1);
        lstIntList.Add(2);
        lstIntList.Add(3);

        lstStrings.Add("a");
        lstStrings.Add("b");
        lstStrings.Add("c");

        dic.Add("Numbers", lstIntList);
        dic.Add("Letters", lstStrings);

        foreach (KeyValuePair<string, object> kvp in dic)
        {
            Console.WriteLine("{0}", kvp.Key);
            lstObjects = ((IEnumerable)kvp.Value).Cast<object>().ToList();

            foreach (var obj in lstObjects)
               {s = obj.ToString(); Console.WriteLine(s);}
            Console.WriteLine("");
        }


        Console.WriteLine("");
        Console.WriteLine("press any key to exit");
        Console.ReadKey();
    }//end main
}
}

enter image description here

Ответ 10

Я пришел к безопасной реализации типа, используя ConditionalWeakTable.

public class FieldByType
{
    static class Storage<T>
        where T : class
    {
        static readonly ConditionalWeakTable<FieldByType, T> table = new ConditionalWeakTable<FieldByType, T>();

        public static T GetValue(FieldByType fieldByType)
        {
            table.TryGetValue(fieldByType, out var result);
            return result;
        }

        public static void SetValue(FieldByType fieldByType, T value)
        {
            table.Remove(fieldByType);
            table.Add(fieldByType, value);
        }
    }

    public T GetValue<T>()
        where T : class
    {
        return Storage<T>.GetValue(this);
    }

    public void SetValue<T>(T value)
        where T : class
    {
        Storage<T>.SetValue(this, value);
    }
}

Это можно использовать так:

/// <summary>
/// This class can be used when cloning multiple related objects to store cloned/original object relationship.
/// </summary>
public class CloningContext
{
    readonly FieldByType dictionaries = new FieldByType();

    public void RegisterClone<T>(T original, T clone)
    {
        var dictionary = dictionaries.GetValue<Dictionary<T, T>>();
        if (dictionary == null)
        {
            dictionary = new Dictionary<T, T>();
            dictionaries.SetValue(dictionary);
        }
        dictionary[original] = clone;
    }

    public bool TryGetClone<T>(T original, out T clone)
    {
        var dictionary = dictionaries.GetValue<Dictionary<T, T>>();
        return dictionary.TryGetValue(original, out clone);
    }
}