首先 - 如果这是一个重复的问题,我会道歉。我确实找到了答案,但是我是一名自学成才的程序员而且我不理解所使用的一些高级术语,因此可能完全错过了实际的答案。
现在,我的问题; 我试图使用Generic方法返回一个打字字典。这是我正在使用的代码。
public Dictionary<string, WorkerValues> workerValues = new Dictionary<string, WorkerValues>();
public Dictionary<string, ValueRefValues> valueRefValues = new Dictionary<string, ValueRefValues>();
public Dictionary<string, LevelValues> levelValues = new Dictionary<string, LevelValues>();
public static T getGainer<T>(string value) where T : GainerValues, new() {
return current.getGainerSub<T>(value); // current is a singleton instance of the class.
}
public static void setGainer<T>(string value, T gain) where T : GainerValues, new() {
current.setGainerSub<T>(value, gain);
}
public T getGainerSub<T>(string value) where T : GainerValues, new() {
Dictionary<string, T> table = getTable <T>();
if (!table.ContainsKey(value)){
table.Add (value, new T());
table[value].setID(value);
}
return (T) table[value];
}
public Dictionary<string, T> getTable<T>() where T : GainerValues, new(){
if (typeof(T) == typeof(WorkerValues)) return workerValues; // Error Here
if (typeof(T) == typeof(ValueRefValues)) return valueRefValues; // Error Here
if (typeof(T) == typeof(LevelValues)) return levelValues; // Error Here
return null;
}
如果T是WokerValues,返回的词典是<string, WorkerValues>
,但编译时出错;
"c:\Users\Aquamentos Games\Documents\Idle Artificer\Assets\Scripts\Data.cs(51,51): Error CS0029: Cannot implicitly convert type 'System.Collections.Generic.Dictionary<string,IdleArtificer.WorkerValues>' to 'System.Collections.Generic.Dictionary<string,T>' (CS0029) (Assembly-CSharp)"
编译器并不知道&#39; T&#39;将是WokerValues&#39;当我试图恢复该值时。
最初,我输入的字典为<string, GainerValues>
,这有效。但是,由于我的实现特定的原因,我不得不改变它们(它与我在会话之间加载和保存数据的方式有关 - 字典需要是合适的类型。)
或者,有更好的方法吗? (请记住,我需要字典类型与此处相同,Dictionary<string, GainerValues>
适用于代码,但它不适用于我正在使用的保存/加载API。
我认为这个帖子与我的问题相似; Get Key and Value types from dictionary in generic method 但是,他似乎试图做一些稍微不同的事情(或者我不理解他的示例代码或答案)
我想过用这里的答案转换字典; Converting a Dictionary from one type to another 但是,如果我不确定这是否是相同的字典&#39;或者如果它是一个新的字典(IE - 我必须将其转换回来并在每次需要添加新密钥时完全更改字典)
编辑以下是整个班级的当前正在使用的版本,并在一个答案中提示了投射更改。
using System;
using System.Collections;
using System.Collections.Generic;
namespace IdleArtificer{
public class Data {
public static Data current = new Data();
public Data(){
// constructor with no parameters needed for I/O API
// also why where T : new() in generic methods
}
public Dictionary<string, WorkerValues> workerValues = new Dictionary<string, WorkerValues>();
public Dictionary<string, ValueRefValues> valueRefValues = new Dictionary<string, ValueRefValues>();
public Dictionary<string, LevelValues> levelValues = new Dictionary<string, LevelValues>();
public static T getGainer<T>(string value) where T : GainerValues, new() {
return current.getGainerSub<T>(value);
}
public static void setGainer<T>(string value, T gain) where T : GainerValues, new() {
current.setGainerSub<T>(value, gain);
}
public Dictionary<string, T> getTable<T>() where T : GainerValues, new(){
if (typeof(T) == typeof(WorkerValues)) return workerValues as Dictionary<string, T>;
if (typeof(T) == typeof(ValueRefValues)) return valueRefValues as Dictionary<string, T>;
if (typeof(T) == typeof(LevelValues)) return levelValues as Dictionary<string, T>;
return null;
}
public T getGainerSub<T>(string value) where T : GainerValues, new() {
Dictionary<string, T> table = getTable <T>();
if (!table.ContainsKey(value)){
table.Add (value, new T());
table[value].setID(value);
}
return (T) table[value];
}
public void setGainerSub<T>(string value, T gain) where T : GainerValues, new() {
Dictionary<string, T> table = getTable <T>();
if (table.ContainsKey(value)) { table[value] = gain; }
else { table.Add(value, gain);}
}
//I can't save enums, so I convert them to/from strings. Int might have
//been better, but I've been changing the order of my enums to sort them, so
//that would mess up any saved data if I make any changes to the order.
public static T ParseEnum<T>(string value) where T : struct, IConvertible
{
if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
return (T) Enum.Parse(typeof(T), value);
}
public static WorkerValues getWorkerValues(string s){
return getGainer<WorkerValues>(s);
}
public static ValueRefValues getValueRefValues(string s){
return getGainer<ValueRefValues>(s);
}
public static LevelValues getLevelValues(string s){
return getGainer<LevelValues>(s);
}
public static WorkerValues getWorkerValues(resource res){
return getWorkerValues(res.ToString());
}
public static ValueRefValues getValueRefValues(valueRef res){
return getValueRefValues(res.ToString());
}
public static LevelValues getLevelValues(level res){
return getLevelValues(res.ToString());
}
}
}
答案 0 :(得分:2)
首先,这段代码显示了代码设计错误的所有迹象(难闻的气味),建议重构它。
所以问题是C#无法在编译时导出,当你输入:
if (typeof(T) == typeof(WorkerValues)) return workerValues;
字典Dictionary<String,WorkerValues>
实际上也是Dictionary<String,T>
。这可能看起来很奇怪,但是在许多情况下,关于代码的复杂推理并不是编译器应该做的事情。人类的理由如“ Haa,if语句成功,所以现在我们知道T
保证是WorkerValues
”。编译器只看到“如果该语句成功 - 我不知道它意味着什么 - 该程序应该返回该。”。编译器(和代码合同验证器存在,可以执行更高级的分析,但C#标准不是这样做的。)
您无法使用 Linq 的ToDictionary
方法,因为这会生成字典的副本。这很昂贵,添加元素不会反映到原始字典中。但是你可以使用演员:
public Dictionary<string, T> getTable<T>() where T : GainerValues, new(){
if (typeof(T) == typeof(WorkerValues)) return workerValues as Dictionary<string,T>;
if (typeof(T) == typeof(ValueRefValues)) return valueRefValues as Dictionary<string,T>;
if (typeof(T) == typeof(LevelValues)) return levelValues as Dictionary<string,T>;
return null;
}
(在每个as Dictionary<string,T>
语句后添加return
。)
as
运算符会执行“安全”转换,因为如果无法转换为Dictionary<string,T>
,它将返回null
。但如前所述,这种混合编译时和运行时泛型是一个很麻烦的来源。
对于这个例子,它可以工作,但执行这样的操作并不是好的设计。