C# 在泛型方法中将值转换为 T
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/856723/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Casting value to T in a generic method
提问by Benjol
I have an interface for a creaky property-map:
我有一个吱吱作响的属性图的界面:
interface IPropertyMap
{
bool Exists(string key);
int GetInt(string key);
string GetString(string key);
//etc..
}
I want to create an extension method like so:
我想创建一个像这样的扩展方法:
public static T GetOrDefault<T>(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..
}
}
But the compiler won't let me cast to T. I tried adding where T : struct
but that doesn't seem to help.
但是编译器不会让我转换为 T。我尝试添加,where T : struct
但这似乎没有帮助。
What am I missing?
我错过了什么?
采纳答案by Jon Skeet
I believe this is because the compiler doesn't know what type of operation it needs to perform. IIRC, you can get it to work if you introduce boxing:
我相信这是因为编译器不知道它需要执行什么类型的操作。IIRC,如果你引入拳击,你可以让它工作:
if (typeof(T) == typeof(int)) return (T)(object)map.GetInt(key);
but that's not ideal in terms of performance.
但这在性能方面并不理想。
I think it's just a limitation of generics, unfortunately.
不幸的是,我认为这只是泛型的限制。
回答by Marc Gravell
What do GetInt
, GetString
etc do internally? There may be other options involving Convert.ChangeType(...)
or TypeDescriptor.GetConverter(...).ConvertFrom(...)
, and a single cast, using an "object" indexer:
做什么GetInt
,GetString
等内部吗?可能还有其他选项涉及Convert.ChangeType(...)
orTypeDescriptor.GetConverter(...).ConvertFrom(...)
和单个强制转换,使用“对象”索引器:
for example, if the objects are already correctly typed:
例如,如果对象已经正确键入:
public T GetOrDefault<T>(this IPropertyMap map, string key, T defaultValue)
{
return map.Exists(key) ? (T)map[key] : defaultValue;
}
or if they are stored as strings and need conversion, something involving:
或者如果它们存储为字符串并需要转换,则涉及:
T typedVal = (T) TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(map[key]);
回答by Anton Gogolev
I suppose it's just a typo, but bool GetInt(string key)
seems weird. It should be int GetInt(string key)
, or better yet int GetInt32(string key)
.
我想这只是一个错字,但bool GetInt(string key)
看起来很奇怪。它应该是int GetInt(string key)
,或者更好int GetInt32(string key)
。
Next, Jon has already noted that boxing is required for your code to work, so this is what you do.
接下来,Jon 已经注意到要让您的代码工作需要装箱,所以这就是您要做的。
And finally, add a "catch-all" method to your IPropertyMap
interface -- say object GetValue(string key)
and then rewrite GetOrDefault<T>
to utilize this method instead of endless and error prone Type
comparisons:
最后,在您的IPropertyMap
界面中添加一个“包罗万象”的方法——比如说object GetValue(string key)
,然后重写GetOrDefault<T>
以利用这个方法,而不是无休止和容易出错的Type
比较:
else
return (T)(object)map.GetValue(key);
回答by Benjol
Just for reference, I discovered another interface which does have GetType() and GetAsObject() methods, which allows me to integrate elements of these answers to do this:
仅供参考,我发现了另一个具有 GetType() 和 GetAsObject() 方法的接口,它允许我集成这些答案的元素来执行此操作:
public static T GetOrDefault<T>(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;
}
(Purists will note that I'm not of the 'braces for one statement' school...)
(纯粹主义者会注意到我不是“一个声明的括号”学校......)
回答by csaam
I don't think this is a good method. You have no way of controlling what T is. For instance
我不认为这是一个好方法。你无法控制 T 是什么。例如
float value = map.GetOrDefault("blah", 2.0);
浮点值 = map.GetOrDefault("blah", 2.0);
won't compile either because Cannot implicitly convert type 'double' to 'float'. An explicit conversion exists (are you missing a cast?) Another failure point is when the desired default value is null. This methods leaves the dev at the mercy of the compiler to resolve what it thinks the dev intends.
也不会编译,因为无法将类型“double”隐式转换为“float”。存在显式转换(您是否缺少强制转换?)另一个失败点是所需的默认值为 null。这种方法让开发人员任由编译器摆布,以解决它认为开发人员的意图。
If you can change the interface then add a GetObject method. Since you are using an extension method I assume you can't so cast to an object then to int. Either way change the method to look like
如果您可以更改接口,则添加 GetObject 方法。由于您使用的是扩展方法,我假设您不能将其转换为对象然后转换为 int。无论哪种方式改变方法看起来像
public static void GetOrDefault(this IPropertyMap map, string key, ref T value) { if (map.Exists(key)) { if (typeof(T) == typeof(int)) { value = (T)(object)map.GetInt(key); } value = default(T); // this is just a nicety because I am lazy, // add real code here. } } and call like this
public static void GetOrDefault(this IPropertyMap map, string key, ref T value) { if (map.Exists(key)) { if (typeof(T) == typeof(int)) { value = (T)(object)map .GetInt(key); } 值=默认(T);// 这只是一个细节,因为我很懒, // 在这里添加真正的代码。并像这样打电话
PropertyMap map = new PropertyMap();
float value = 2.0f;
map.GetOrDefault("blah", ref value);
I hate ref params but I see the point here. The registry is a classic example of when this kind of method is usefull. The code above forces the dev user to explicity specify the type of the output and preserves the concept default value.
我讨厌 ref params 但我在这里看到了重点。注册表是这种方法何时有用的经典示例。上面的代码强制 dev 用户明确指定输出的类型并保留概念默认值。