在generics方法中将值转换为T.
我有一个creaky属性映射的接口:
interface IPropertyMap { bool Exists(string key); int GetInt(string key); string GetString(string key); //etc.. }
我想创建一个像这样的扩展方法:
public static T GetOrDefault(this IPropertyMap map, string key, T defaultValue) { if (!map.Exists(key)) return defaultValue; else { if (typeof(T) == typeof(int)) return (T)map.GetInt(key); //etc.. } }
但编译器不会让我转向T.我尝试添加where T : struct
但这似乎没有帮助。
我错过了什么?
我相信这是因为编译器不知道它需要执行什么类型的操作。 IIRC,如果你介绍拳击,你可以让它工作:
if (typeof(T) == typeof(int)) return (T)(object)map.GetInt(key);
但就性能而言,这并不理想。
不幸的是,我认为这只是generics的限制。
GetInt
, GetString
等在内部做什么? 可能有其他选项涉及Convert.ChangeType(...)
或TypeDescriptor.GetConverter(...).ConvertFrom(...)
和单个强制转换,使用“对象”索引器:
例如,如果对象已经正确输入:
public T GetOrDefault(this IPropertyMap map, string key, T defaultValue) { return map.Exists(key) ? (T)map[key] : defaultValue; }
或者如果它们存储为字符串并需要转换,则涉及:
T typedVal = (T) TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(map[key]);
我想这只是一个错字,但bool GetInt(string key)
似乎很奇怪。 它应该是int GetInt(string key)
,或者更好的是int GetInt32(string key)
。
接下来,Jon已经注意到你的代码需要装箱,所以这就是你所做的。
最后,在IPropertyMap
接口中添加一个“catch-all”方法 – 比如object GetValue(string key)
然后重写GetOrDefault
以使用此方法而不是无限且容易出错的Type
比较:
else return (T)(object)map.GetValue(key);
仅供参考,我发现了另一个具有GetType()和GetAsObject()方法的接口,它允许我集成这些答案的元素来执行此操作:
public static T GetOrDefault(this IInfosContainer container, string key, T defaultValue) { //I just read p273 of C# in Depth, +1 Jon Skeet :) if (container == null) throw new ArgumentNullException("container"); if (container.Exist(key)) { if (container.GetType(key) != typeof(T)) throw new ArgumentOutOfRangeException("key", "Key exists, but not same type as defaultValue parameter"); else return (T)container.GetAsObject(key); } else return defaultValue; }
( 纯粹主义者会注意到我不是’一个声明’学校的’大括号…… )
我认为这不是一个好方法。 你无法控制T是什么。 例如
float value = map.GetOrDefault(“blah”,2.0);
不会编译,因为不能隐式地将类型’double’转换为’float’。 存在显式转换(您是否缺少转换?)另一个失败点是所需的默认值为null。 这种方法使开发人员受编译器的支配,以解决它认为开发人员的意图。
如果可以更改接口,则添加GetObject方法。 由于您使用的是扩展方法,我假设您不能将对象强制转换为int。 无论哪种方式改变方法看起来像
public static void GetOrDefault(此IPropertyMap映射,字符串键,ref T值){if(map.Exists(key)){if(typeof(T)== typeof(int)){value =(T)(object)map .GetInt(键); } value = default(T); //这只是一个非常好的因为我很懒,//在这里添加实际代码。 并且这样调用
PropertyMap map = new PropertyMap(); float value = 2.0f; map.GetOrDefault("blah", ref value);
我讨厌ref params,但我明白了这一点。 注册表是这种方法何时有用的经典示例。 上面的代码强制dev用户明确指定输出的类型并保留概念默认值。