29

在运行时,我得到某种类型的盒装实例。如何将其拆箱为基础类型?

Object obj;
String variable = "Some text";

obj = variable // boxing;

// explicit unboxing, because we know the type of variable at compile time.

var x = (String)obj     

// Now let's pretend that we don't know the type of underlying object at compile time. 

Type desiredType = obj.GetType(); // But we can figure out.

//And now the question. 
//How to express something like this:

var y = (desiredType)obj; //Need to get unboxed instance of initial variable here; 
4

8 回答 8

23

If you don't know the type at compile time, then you can't unbox because you have nowhere to put it - all you can do is store it in an object, which is: boxed.

The same also applies to reference-types like string: you can't cast it to the right type if you don't know the type at compile time: you have nowhere to put it.

You can special-case a few types, for example:

if(obj is int) {
    int i = (int)obj;
    ...
} ...

Another trick that is sometimes (not often) helpful is to switch into generics; then instead of talking in terms of object you are talking in terms of T. This has... limited use though. The easiest way to do that is via dynamic, for example:

dynamic obj = ...
Foo(obj);
...
Foo<T>(T val) { ... code with T ... }

you can also add special cases to that appreach:

Foo(string val) { ... code with string ...}
Foo(int val) { ... code with int ...}

However, frankly I suggest it may be better to look hard at what you are trying to do.

于 2013-03-22T07:47:58.987 回答
10

现在让我们假设,真正的拳击发生了:

int v = 5;

object o = v; //boxed 

Type type = o.GetType(); //will return typeof(int)

int convertedBack = (int)Convert.ChangeType(o, type);

Console.WriteLine (convertedBack); //prints 5

备注,如果你替换:

object convertedBack = Convert.ChangeType(o, type);

Console.WriteLine (convertedBack); //it still prints 5
Console.WriteLine (o); //it even print 5 here

原因是底层对象仍然是int. 我刚刚用这个例子向你展示了,拳击在这里是无关紧要的。你需要在你的操作中依赖一些抽象,如果你想int动态转换,你想使用什么引用类型。

于 2013-03-22T07:45:30.787 回答
5

在这种情况下,我将使用以下策略模式Dictionary<Type, Action<object>>

internal class Program
{
    private static void Main(string[] args)
    {
        var something = new Something();

        something.ComputeValue(13);
        something.ComputeValue(DateTime.Now);
        something.ComputeValue(DayOfWeek.Monday);

        Console.ReadKey();
    }
}

internal class Something
{
    private static Dictionary<Type, Action<object>> _Strategies;

    static Something()
    {
        // Prepare all available strategies.
        _Strategies = new Dictionary<Type, Action<object>>();
        _Strategies.Add(typeof(int), ComputeInteger);
        _Strategies.Add(typeof(DateTime), ComputeDateTime);
    }

    public void ComputeValue(object value)
    {
        Action<object> action;

        // Check if we have a matching strategy.
        if (!_Strategies.TryGetValue(value.GetType(), out action))
        {
            // If not, log error, throw exception, whatever.
            action = LogUnknownType;
        }

        // Perform the matching strategy on the given value.
        action(value);
    }

    private static void ComputeDateTime(object source)
    {
        // We get an object, but we are sure that it will always be an DateTime.
        var value = (DateTime)source;
        Console.WriteLine("We've got an date time: " + value);
    }

    private static void ComputeInteger(object source)
    {
        // We get an object, but we are sure that it will always be an int.
        var value = (int)source;
        Console.WriteLine("We've got an integer: " + value);
    }

    private static void LogUnknownType(object source)
    {
        // What should we do with the drunken sailor?
        var unknownType = source.GetType();
        Console.WriteLine("Don't know how to handle " + unknownType.FullName);
    }
}
于 2013-03-22T08:31:19.313 回答
1

以下是您为什么要这样做的一个示例:

class MyClass
{
   public int Id {get;set;}
   public string Name {get;set;}
   public decimal Val {get;set;}
}
int i = 0;
var myClassImp = new MyClass();
foreach (var val in new [object]{"10", "My name", "100.21"} // Could be read from some data source, such as an excel spreadsheet
{
   var prop = typeof(MyClass).GetProperties().ElementAt(i++);
   // !!!!!! THROWS EXCEPTION  !!!!!!!
   prop.SetValue(myClassImp, System.Convert.ChangeType(val, prop.PropertyType), null);
}

这样做的原因是因为该值是一个装箱的对象......在运行时你不知道类型,所以你必须取消装箱到 prop.PropertyType

于 2013-10-16T15:36:18.970 回答
1

使用表达式:

var y = DynamicCast(obj, desiredType);

static object DynamicCast(object source, Type type)
{
    var parameter = Expression.Parameter(typeof(object), "input");

    var cast = Expression.TypeAs(Expression.Convert(parameter, type), typeof(object));

    var lambda = Expression.Lambda<Func<object, object>>(cast, parameter);

    var func = lambda.Compile();

    return func(source);
}
于 2016-12-08T08:18:08.000 回答
1

务实的解决方案;尝试直接使用 TypeConverter,如果失败,则转换为字符串并再次返回:-

private static T GetValueOfType<T>(this ManagementBaseObject MBO, String FieldName) {
    T lResult;

    try {
        Object lObj = MBO[FieldName];

        var lSrcType = lObj.GetType();
        var lDestType = typeof(T);

        if (lDestType.IsValueType && lDestType.IsAssignableFrom(lSrcType)) {
            lResult = (T)lObj;
            return lResult;
        }

        var lDestTC = TypeDescriptor.GetConverter(typeof(T));
        if (lDestTC.CanConvertFrom(lSrcType)) {
            lResult = (T)lDestTC.ConvertFrom(lObj);
        } else {
            var lSrcTC = TypeDescriptor.GetConverter(lSrcType);
            String lTmp = lSrcTC.ConvertToInvariantString(lObj);
            lResult = (T)lDestTC.ConvertFromInvariantString(lTmp);
        }
    } catch {
        lResult = default(T);
    }
    return lResult;
}
于 2015-11-22T10:53:07.807 回答
0
public static string GetType(object data)
{
    Type type = data.GetType();
    return Convert.ChangeType(data, type).GetType().Name;
}

您好,此方法接收对象数据并返回对象的字符串类型名称。希望这是你需要的。

于 2016-03-24T07:45:26.340 回答
-1

您可以尝试使用动态运行时

    [Test]
    public void Test_UnboxingAtRuntime()
    {
        object boxed = "Hello";

        //this line is commented out as it does not compile
        // OverloadedMethod(boxed);

        var result = CallCorrectMethod(boxed);
        Assert.That(result, Is.EqualTo("string"));

        boxed = 1;
        result = CallCorrectMethod(boxed);
        Assert.That(result, Is.EqualTo("int"));
    }

    public string CallCorrectMethod(dynamic t)
    {
        return OverloadedMethod(t);
    }

    public string OverloadedMethod(string s)
    {
        return "string";
    }

    public string OverloadedMethod(int s)
    {
        return "int";
    }
于 2013-03-22T08:01:11.470 回答