Это скорее академический вопрос, чем практический. Существуют ли какие-либо особенности языка или фреймворка, которые могут или будут в будущем допускать использование гетерогенного типизированного рабочего места, например
myDict.Add("Name", "Bill");
myDict.Add("Height", 1.2);
где myDict теперь содержит в качестве значений не два типа object, а один string и один double? Тогда я мог бы получить свой double с помощью
double dbl = myDict["Height"];
и ожидаете, что будет сгенерировано двойное или исключение?
Обратите внимание: значения Name и Height не обязательно относятся к одному и тому же объекту.





Вы можете использовать Generic Dictionary<object, object>; тип объекта для ключа и тип объекта для значения. C# может помочь.
По какой причине вы не можете создать класс / структуру и поместить это в список или словарь? В противном случае вы могли бы просто обернуть класс словаря своим собственным и установить ограничения таким образом.
Почему бы просто не сохранить все значения в виде строк, преобразовать их при их извлечении и перехватить исключения из этого преобразования?
Я не понимаю, почему вам нужно делать что-то таким образом - возможно, стоит переделать вашу программу?
Ключи все струны? тогда Dictionary<string,object>, вероятно, выполнит эту работу ... но при получении значений вам либо нужно будет использовать object, либо вам нужно будет преобразовать:
double dbl = (double)myDict["Height"];
object height = myDict["Height"];
Причина, по которой я задал вопрос, заключалась в том, чтобы избежать использования значений dic как объектов. Если я сохраняю двойное, мне нужно двойное назад, а не «объект с двойной экспозицией», который я должен использовать.
@Profk - вы обманываете себя, если думаете, что Get <int> (...) отличается от (int) Get (...). Ну что ж...
@MarcGravell неправда. Если класс внутренне обеспечивает связь между типом и его значением при вставке, то извлечение, требующее внутреннего приведения, будет полностью безопасным (небезопасным для компилятора, но безопасным при условии, что код является внутренне правильным). Никакой внешний вызывающий объект не будет способный, чтобы вызвать недопустимое приведение на основе несоответствия типа ключ / значение. Они либо получат типизированное значение, либо вернут нулевое значение, и это будет полностью типобезопасно с их точки зрения. Теоретически недопустимые исключения при приведении типов никогда не возникнут ни внутри контейнера, ни за его пределами.
@AaronHS Я не понимаю, как это вообще имеет значение; в этом случае и (int)Get(...), и Get<int>(...) будут работать, но они уже работают (или нет) вместе. Все, что мы сделали, это переместили гипс. Возвращать null, потому что кто-то ошибся в типе, - плохой ход; нужно сделать правильный: недопустимое исключение приведения.
@MarcGravell проблема не в том, что somebody got the type wrong. Проблема в том, что кто-то наивно связал ключ со значением одного типа, скажем, string: (1) dict.Add("myKey!", "myStringValue!"). Позже тот же кто-то пытается получить это значение: (2) возникает исключение приведения типов string unsafeStr = (string)dict["myKey!"] и хлопнуть. Но почему? Другой считал, что myKey! - отличный ключ для значения int, и где-то между (1) и (2) была выполнена эта строка кода: dict["myKey!"] = 123. Здесь (string)Get терпит неудачу, но Get<String> преуспевает и сохраняет согласованность.
@yair и я утверждаем, что лучшее, что может здесь случиться - это сбой, чтобы предупредить вас о проблеме; очень редко хорошие дела возникают из-за игнорирования проблем.
@MarcGravell, поскольку я пришел с Java, я должен сказать, что отдельные методы для добавления нового значения или перезаписи существующего в словарь - могут оказаться большим подспорьем для предотвращения случая, о котором я упоминал в предыдущем комментарии. В Java за обе ошибки отвечает один и тот же метод put, поэтому он более подвержен ошибкам такого рода. Итак, в C# я действительно не уверен, что эти хлопоты стоят небольшого выигрыша в безопасности типов.
@MarcGravell Я в целом согласен. Причиной написания такого специального служебного класса должна быть не слабая попытка предотвратить какую-либо маловероятную ошибку (или вероятную), а то, что он добавляет некоторые необходимые функции, которые отсутствуют в основных библиотеках. Итак, +1! (Я потратил часы на поиск лучшего решения)
Если типы являются классами, ничто не мешает вам взаимодействовать с обоими и помещать экземпляры в словарь <foo, IYourInterface>
Может быть. Может быть, что-то вроде IPresentationElementValue. Пища для размышлений.
Словарь БУДЕТ содержать строку и двойное значение в качестве значений, но двойное будет заключено в рамку.
Как компилятор мог проверить то, о чем вы спрашиваете? Как он мог узнать во время компиляции, какой тип объекта относится к каждому ключу?
Идея состоит не в том, чтобы знать во время компиляции, а в том, чтобы использовать ключ для определения возвращаемого типа. C# движется к включению некоторых захватывающих динамических концепций, и я полагаю, что это может остаться незамеченным.
Словарь не подходит для этого. Вам действительно нужен класс Person для хранения этих значений.
И с помощью классов намного проще создавать коллекции и вылавливать ошибки.
Обновлено:
Поскольку вам нужен полу-строго типизированный dict:
public class CustomDictionary : Dictionary<string,object>
{
public T GetTyped<T>(string key) where T : IConvertible
{
return (T)Convert.ChangeType(base[key], typeof (T));
}
}
Использование:
CustomDictionary dictionary = new CustomDictionary();
dictionary.Add("Test",1.2);
var d = dictionary.GetTyped<double>("Test");
Значения не обязательно должны быть связаны с одним и тем же объектом, как это случайно подразумевается в моем примере. Имя могло быть как у собаки, а Рост - как у стола.
Ох, хорошо. Вам нужен CustomDictonary, который расширяет Dictionary <string, object> и добавляет метод GetTyped <Type> (строковый ключ) :)
Единственный способ сделать это, если у вас есть настраиваемая коллекция с универсальными перегрузками для методов Add и Get. Но это будет означать, что вы можете запросить неправильный тип при чтении ключа, поэтому это не принесет вам много (если вообще что-либо), если вы сами выполняете приведение, когда вы вызываете свой метод Get.
Однако, если вы можете вставить общий тип в ключ, это может сработать. Что-то вроде (здесь непроверенный код)
sealed class MyDictionaryKey<T>
{
}
class MyDictionary
{
private Dictionary<object, object> dictionary = new Dictionary<object, object>();
public void Add<T>(MyDictionaryKey<T> key, T value)
{
dictionary.Add(key, value);
}
public bool TryGetValue<T>(MyDictionaryKey<T> key, out T value)
{
object objValue;
if (dictionary.TryGetValue(key, out objValue))
{
value = (T)objValue;
return true;
}
value = default(T);
return false;
}
public T Get<T>(MyDictionaryKey<T> key)
{
T value;
if (!TryGetValue(key, out value))
throw new KeyNotFoundException();
return value;
}
}
Затем вы можете определить свои ключи следующим образом:
static readonly MyDictionaryKey<string> NameKey = new MyDictionaryKey<string>();
static readonly MyDictionaryKey<double> HeightKey = new MyDictionaryKey<double>();
и используйте это как
var myDict = new MyDictionary();
myDict.Add(NameKey, "Bill"); // this will take a string
myDict.Add(HeightKey , 1.2); // this will take a double
string name = myDict.Get(NameKey); // will return a string
double height = myDict.Get(HeightKey); // will return a double
Спасибо. Как говорили другие, это не столько практическое, сколько академическое исследование, но вы попадаете в цель, сделав вывод, что мне нужен ключ для определения ожидаемого типа извлеченного элемента.
Мне не нужно так поступать. Мне не нужно ничего делать, кроме как исследовать, что можно сделать.