ChatGPT解决这个技术问题 Extra ChatGPT

使用 Type 变量强制转换变量

在 C# 中,我可以将 object 类型的变量转换为 T 类型的变量,其中 TType 变量中定义?

不是严格意义上的主题,但是您似乎对“强制转换”的含义足够模糊,因此准确理解强制转换运算符的目的和语义可能是一个好主意。这是一个好的开始:blogs.msdn.com/ericlippert/archive/2009/03/19/…
我以为我想出了点什么。如果您有 Type 变量,则可以使用反射来创建该类型的实例。然后您可以使用泛型方法通过从该类型的参数推断它来返回您想要的类型。不幸的是,任何创建类型实例的反射方法的返回类型都是 object,因此您的通用 CastByExample 方法也将使用 object。所以真的没有办法做到这一点,即使有,你会如何处理新铸造的对象?你不能使用它的方法或任何东西,因为你不知道它的类型。
@KyleDelaney 谢谢,我完全同意!正如我试图在我的回答中解释的那样,将 something 转换为 不同的东西 而不在某些时候定义您实际使用的 Type 并不是那么有用。类型的重点是编译器时类型检查。如果您只需要对对象进行调用,则可以使用 objectdynamic。如果要动态加载外部模块,可以让类共享一个公共接口并将对象转换为该接口。如果您不控制第三方代码,请创建小型包装器并在其上实现接口。

Y
Yvo

这是一个强制转换和转换的示例:

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:

一些额外的提示:

尽量保持你的代码类型安全。如果编译器不知道类型,那么它就无法检查您的代码是否正确,并且诸如自动完成之类的功能将无法正常工作。简单地说:如果您无法在编译时预测类型,那么编译器将如何能够预测?

如果您正在使用的类实现了一个通用接口,您可以将该值强制转换为该接口。否则考虑创建自己的接口并让类实现该接口。

如果您正在使用动态导入的外部库,那么还要检查一个通用接口。否则考虑创建实现接口的小型包装类。

如果您想对对象进行调用,但不关心类型,则将值存储在对象或动态变量中。

泛型可以是创建适用于许多不同类型的可重用代码的好方法,而不必知道所涉及的确切类型。

如果您遇到困难,请考虑使用不同的方法或代码重构。你的代码真的必须是动态的吗?它是否必须考虑存在的任何类型?


我不知道这对 OP 有什么帮助。她有一个类型变量,而不是 T 本身。
@nawfal,基本上 Convert.ChangeType(input, typeof(T)); 行给出了解决方案。您可以轻松地将 typeof(T) 替换为现有类型变量。一个更好的解决方案(如果可能的话)是一起防止动态类型。
@Zyphrax,不,它仍然需要强制转换为 T ,这是不可用的。
我知道生成的对象确实是 T 类型,但您仍然只能获得 object 作为参考。嗯,我发现这个问题很有趣,前提是 OP 只有 Type 变量而没有其他信息。好像方法签名是 Convert(object source, Type destination) :) 不过我明白了
这是如何解决这个问题的?我有同样的问题,我没有通用的 。我只有一个类型变量。
m
maulik13

其他答案未提及“动态”类型。因此,要再添加一个答案,您可以使用“动态”类型来存储生成的对象,而无需使用静态类型转换转换后的对象。

dynamic changedObj = Convert.ChangeType(obj, typeVar);
changedObj.Method();

请记住,使用“动态”编译器会绕过静态类型检查,如果您不小心,可能会引入可能的运行时错误。

此外,假设 obj 是 TypeVar 的实例或可转换为该类型。


这是正确的答案。没有动态关键字 typeof(changedObj) 是“对象”。使用 dynamic 关键字,它可以完美地工作,并且 typeof(changedObject) 正确地反映了与 typeVar 相同的类型。此外,如果您不知道类型,则不需要 (T) 强制转换。
使用此解决方案时出现“对象必须实现 IConvertible”异常。有什么帮助吗?
@NuriTasdemir 很难说,但我相信如果没有 IConvertible,您正在进行的转换是不可能的。您的转换涉及哪些类型?
虽然这可行,但使用动态会降低性能。我建议不要使用它们,除非您正在使用其他运行时(这是动态设计的目的)。
这怎么是答案? 99.99% 的类型没有实现 IConvertible,所以在 99.99% 的情况下它不会工作。
b
balage

这是我将对象转换为泛型类型变量而不是动态转换为 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;
对我来说,这与 Zyphrax 的回答存在同样的问题。我无法在返回的对象上调用方法,因为它仍然是“对象”类型。无论我使用他的方法(下面的“a”)还是您的方法(下面的“b”),我都会在 (t) 强制转换中得到相同的错误 - “'t' 是一个变量,但它像类型一样使用。Type t = typeof(MyGeneric<>).MakeGenericType(obj.OutputType); var a = (t)Convert.ChangeType(obj, t); var b = (t)Caster.Cast(t, obj);
@muusbolla Zyphrax 的原始答案使用泛型和类型变量,而不是 Type。如果您只有 Type 对象,则不能使用正常的转换语法进行转换。如果您希望能够在编译时而不是运行时将对象用作某种类型 T,则需要使用类型变量或仅使用实际类型名称对其进行转换。您可以使用 Zaphrax 的答案来做前者。
m
mmx

为简单起见,将装箱和拆箱放在一边,沿继承层次结构进行强制转换不涉及特定的运行时操作。这主要是编译时的事情。本质上,强制转换告诉编译器将变量的值视为另一种类型。

选角后你能做什么?你不知道类型,所以你不能调用它的任何方法。你不会有什么特别的事情可以做。具体来说,只有在编译时知道可能的类型、手动转换并使用 if 语句分别处理每种情况时,它才有用:

if (type == typeof(int)) {
    int x = (int)obj;
    DoSomethingWithInt(x);
} else if (type == typeof(string)) {
    string s = (string)obj;
    DoSomethingWithString(s);
} // ...

您能否就我的问题更清楚地解释一下?
我想解释的是,在那之后你能做什么?您无能为力,因为 C# 编译器需要静态类型才能对对象执行有用的操作
你是对的。我知道作为“对象”类型发送到方法的两个变量的预期类型。我想转换为存储在变量中的预期类型,并将它们添加到集合中。更容易在类型上进行分支并尝试正常转换并捕获错误。
你的回答很好,但只是为了挑剔,我注意到强制转换永远不会影响变量。将变量强制转换为另一种类型的变量是不合法的;变量类型在 C# 中是不变的。您只能将存储在变量中的值转换为另一种类型。
C# 4.0 引入的动态类型是否会改变这个答案?
D
Daniel Brückner

你怎么能做到这一点?您需要一个类型为 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?

如果您使用的是泛型类,它定义了一个返回值类型为 T 的方法,您可能需要这样做。例如,将字符串解析为 T 的实例并返回它。
幸运的是,这不是正确的答案。请参阅maulik13 的答案。
天哪,您在 Object 上的什么地方找到了 CastTo 方法?
C
Curt

在使用 Zyphrax 的答案(实现接口除外)时没有找到任何解决“对象必须实现 IConvertible”异常的方法之后。我尝试了一些非常规的方法并为我的情况工作。

使用 Newtonsoft.Json nuget 包...

var castedObject = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(myObject), myType);

这适用于对象,但类呢?
k
krzyski

当谈到强制转换为 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 转换为给定的枚举不是更简单的方法吗?像这样的东西:int value = 3; var yourEnum = (YourEnum) value;
@KamilZ tbh 我不完全记得这个具体情况,我认为使用普通转换你将无法访问枚举属性stackoverflow.com/questions/37891724/…我不再专业地使用 C#。
L
Leye Eltee Taiwo

我永远不会明白为什么您需要多达 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 );

对于遇到这种情况的任何人来说,即使与反射相比,这种方法也会非常慢,因为 JsonConvert 在幕后所做的不仅仅是转换这些值!
b
bluish
public bool TryCast<T>(ref T t, object o)
{
    if (
        o == null
        || !typeof(T).IsAssignableFrom(o.GetType())
        )
        return false;
    t = (T)o;
    return true;
}

您能否指出此答案与其他答案有何不同以及该解决方案适用于何处?
m
marianop

如果您需要在运行时转换对象而不知道目标类型,您可以使用反射来制作动态转换器。

这是一个简化版本(没有缓存生成的方法):

    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));

H
Harm Salomons

更清洁:

    public static bool TryCast<T>(ref T t, object o)
    {
        if (!(o is T))
        {
            return false;
        }

        t = (T)o;
        return true;
    }