在 C# 中,我可以将 object
类型的变量转换为 T
类型的变量,其中 T
在 Type
变量中定义?
Type
变量,则可以使用反射来创建该类型的实例。然后您可以使用泛型方法通过从该类型的参数推断它来返回您想要的类型。不幸的是,任何创建类型实例的反射方法的返回类型都是 object
,因此您的通用 CastByExample
方法也将使用 object
。所以真的没有办法做到这一点,即使有,你会如何处理新铸造的对象?你不能使用它的方法或任何东西,因为你不知道它的类型。
object
或 dynamic
。如果要动态加载外部模块,可以让类共享一个公共接口并将对象转换为该接口。如果您不控制第三方代码,请创建小型包装器并在其上实现接口。
这是一个强制转换和转换的示例:
using System;
public T CastObject<T>(object input) {
return (T) input;
}
public T ConvertObject<T>(object input) {
return (T) Convert.ChangeType(input, typeof(T));
}
编辑:
评论中的一些人说这个答案没有回答这个问题。但是行 (T) Convert.ChangeType(input, typeof(T))
提供了解决方案。 Convert.ChangeType
方法尝试将任何 Object 转换为作为第二个参数提供的 Type。
例如:
Type intType = typeof(Int32);
object value1 = 1000.1;
// Variable value2 is now an int with a value of 1000, the compiler
// knows the exact type, it is safe to use and you will have autocomplete
int value2 = Convert.ChangeType(value1, intType);
// Variable value3 is now an int with a value of 1000, the compiler
// doesn't know the exact type so it will allow you to call any
// property or method on it, but will crash if it doesn't exist
dynamic value3 = Convert.ChangeType(value1, intType);
我已经用泛型编写了答案,因为我认为当您想在不处理实际类型的情况下将 a something
强制转换为 a something else
时,这很可能是代码异味的迹象。使用适当的接口,在 99.9% 的情况下都不需要。当涉及到反射时,可能有一些边缘情况可能有意义,但我建议避免这些情况。
编辑2:
一些额外的提示:
尽量保持你的代码类型安全。如果编译器不知道类型,那么它就无法检查您的代码是否正确,并且诸如自动完成之类的功能将无法正常工作。简单地说:如果您无法在编译时预测类型,那么编译器将如何能够预测?
如果您正在使用的类实现了一个通用接口,您可以将该值强制转换为该接口。否则考虑创建自己的接口并让类实现该接口。
如果您正在使用动态导入的外部库,那么还要检查一个通用接口。否则考虑创建实现接口的小型包装类。
如果您想对对象进行调用,但不关心类型,则将值存储在对象或动态变量中。
泛型可以是创建适用于许多不同类型的可重用代码的好方法,而不必知道所涉及的确切类型。
如果您遇到困难,请考虑使用不同的方法或代码重构。你的代码真的必须是动态的吗?它是否必须考虑存在的任何类型?
其他答案未提及“动态”类型。因此,要再添加一个答案,您可以使用“动态”类型来存储生成的对象,而无需使用静态类型转换转换后的对象。
dynamic changedObj = Convert.ChangeType(obj, typeVar);
changedObj.Method();
请记住,使用“动态”编译器会绕过静态类型检查,如果您不小心,可能会引入可能的运行时错误。
此外,假设 obj 是 TypeVar 的实例或可转换为该类型。
这是我将对象转换为泛型类型变量而不是动态转换为 System.Type
的方法:
我在运行时使用 Func<object, object>
类型的 System.Linq.Expressions
创建了一个 lambda 表达式,该表达式将其输入拆箱,执行所需的类型转换,然后将结果装箱。不仅要转换为所有类型,而且要转换的类型(因为拆箱步骤)都需要一个新的。创建这些表达式非常耗时,因为反射、编译和动态方法构建是在后台完成的。幸运的是,一旦创建,表达式可以被重复调用并且没有高开销,所以我缓存了每一个。
private static Func<object, object> MakeCastDelegate(Type from, Type to)
{
var p = Expression.Parameter(typeof(object)); //do not inline
return Expression.Lambda<Func<object, object>>(
Expression.Convert(Expression.ConvertChecked(Expression.Convert(p, from), to), typeof(object)),
p).Compile();
}
private static readonly Dictionary<Tuple<Type, Type>, Func<object, object>> CastCache
= new Dictionary<Tuple<Type, Type>, Func<object, object>>();
public static Func<object, object> GetCastDelegate(Type from, Type to)
{
lock (CastCache)
{
var key = new Tuple<Type, Type>(from, to);
Func<object, object> cast_delegate;
if (!CastCache.TryGetValue(key, out cast_delegate))
{
cast_delegate = MakeCastDelegate(from, to);
CastCache.Add(key, cast_delegate);
}
return cast_delegate;
}
}
public static object Cast(Type t, object o)
{
return GetCastDelegate(o.GetType(), t).Invoke(o);
}
请注意,这不是魔术。转换不会像使用 dynamic
关键字那样发生在代码中,只会转换对象的基础数据。在编译时,我们仍然需要费力地弄清楚我们的对象可能是什么类型,这使得这个解决方案不切实际。我写这个是为了调用由任意类型定义的转换运算符,但也许有人可以找到更好的用例。
using System.Linq.Expressions;
Type t = typeof(MyGeneric<>).MakeGenericType(obj.OutputType); var a = (t)Convert.ChangeType(obj, t); var b = (t)Caster.Cast(t, obj);
Type
。如果您只有 Type 对象,则不能使用正常的转换语法进行转换。如果您希望能够在编译时而不是运行时将对象用作某种类型 T,则需要使用类型变量或仅使用实际类型名称对其进行转换。您可以使用 Zaphrax 的答案来做前者。
为简单起见,将装箱和拆箱放在一边,沿继承层次结构进行强制转换不涉及特定的运行时操作。这主要是编译时的事情。本质上,强制转换告诉编译器将变量的值视为另一种类型。
选角后你能做什么?你不知道类型,所以你不能调用它的任何方法。你不会有什么特别的事情可以做。具体来说,只有在编译时知道可能的类型、手动转换并使用 if
语句分别处理每种情况时,它才有用:
if (type == typeof(int)) {
int x = (int)obj;
DoSomethingWithInt(x);
} else if (type == typeof(string)) {
string s = (string)obj;
DoSomethingWithString(s);
} // ...
你怎么能做到这一点?您需要一个类型为 T 的变量或字段,您可以在其中存储转换后的对象,但如果您只在运行时知道 T,您如何拥有这样的变量或字段?所以,不,这是不可能的。
Type type = GetSomeType();
Object @object = GetSomeObject();
??? xyz = @object.CastTo(type); // How would you declare the variable?
xyz.??? // What methods, properties, or fields are valid here?
Object
上的什么地方找到了 CastTo
方法?
在使用 Zyphrax 的答案(实现接口除外)时没有找到任何解决“对象必须实现 IConvertible”异常的方法之后。我尝试了一些非常规的方法并为我的情况工作。
使用 Newtonsoft.Json nuget 包...
var castedObject = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(myObject), myType);
当谈到强制转换为 Enum 类型时:
private static Enum GetEnum(Type type, int value)
{
if (type.IsEnum)
if (Enum.IsDefined(type, value))
{
return (Enum)Enum.ToObject(type, value);
}
return null;
}
你会这样称呼它:
var enumValue = GetEnum(typeof(YourEnum), foo);
如果通过 int 值获取多个枚举类型的 Description 属性值,这对我来说是必不可少的:
public enum YourEnum
{
[Description("Desc1")]
Val1,
[Description("Desc2")]
Val2,
Val3,
}
public static string GetDescriptionFromEnum(Enum value, bool inherit)
{
Type type = value.GetType();
System.Reflection.MemberInfo[] memInfo = type.GetMember(value.ToString());
if (memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), inherit);
if (attrs.Length > 0)
return ((DescriptionAttribute)attrs[0]).Description;
}
return value.ToString();
}
接着:
string description = GetDescriptionFromEnum(GetEnum(typeof(YourEnum), foo));
string description2 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum2), foo2));
string description3 = GetDescriptionFromEnum(GetEnum(typeof(YourEnum3), foo3));
或者(更好的方法),这样的转换可能看起来像这样:
private static T GetEnum<T>(int v) where T : struct, IConvertible
{
if (typeof(T).IsEnum)
if (Enum.IsDefined(typeof(T), v))
{
return (T)Enum.ToObject(typeof(T), v);
}
throw new ArgumentException(string.Format("{0} is not a valid value of {1}", v, typeof(T).Name));
}
int value = 3; var yourEnum = (YourEnum) value;
我永远不会明白为什么您需要多达 50 个声望才能发表评论,但我不得不说@Curt 的答案正是我所期待的,希望是其他人。
在我的示例中,我有一个 ActionFilterAttribute 用于更新 json 补丁文档的值。我不知道补丁文档的 T 模型是什么,我必须将它序列化和反序列化为普通的 JsonPatchDocument,修改它,然后因为我有类型,再次将它序列化和反序列化回类型。
Type originalType = //someType that gets passed in to my constructor.
var objectAsString = JsonConvert.SerializeObject(myObjectWithAGenericType);
var plainPatchDocument = JsonConvert.DeserializeObject<JsonPatchDocument>(objectAsString);
var plainPatchDocumentAsString= JsonConvert.SerializeObject(plainPatchDocument);
var modifiedObjectWithGenericType = JsonConvert.DeserializeObject(plainPatchDocumentAsString, originalType );
public bool TryCast<T>(ref T t, object o)
{
if (
o == null
|| !typeof(T).IsAssignableFrom(o.GetType())
)
return false;
t = (T)o;
return true;
}
如果您需要在运行时转换对象而不知道目标类型,您可以使用反射来制作动态转换器。
这是一个简化版本(没有缓存生成的方法):
public static class Tool
{
public static object CastTo<T>(object value) where T : class
{
return value as T;
}
private static readonly MethodInfo CastToInfo = typeof (Tool).GetMethod("CastTo");
public static object DynamicCast(object source, Type targetType)
{
return CastToInfo.MakeGenericMethod(new[] { targetType }).Invoke(null, new[] { source });
}
}
那么你可以调用它:
var r = Tool.DynamicCast(myinstance, typeof (MyClass));
更清洁:
public static bool TryCast<T>(ref T t, object o)
{
if (!(o is T))
{
return false;
}
t = (T)o;
return true;
}
T
本身。Convert.ChangeType(input, typeof(T));
行给出了解决方案。您可以轻松地将typeof(T)
替换为现有类型变量。一个更好的解决方案(如果可能的话)是一起防止动态类型。T
,这是不可用的。T
类型,但您仍然只能获得object
作为参考。嗯,我发现这个问题很有趣,前提是 OP 只有Type
变量而没有其他信息。好像方法签名是Convert(object source, Type destination)
:) 不过我明白了