【问题标题】:Unboxing to unknown type拆箱到未知类型
【发布时间】:2010-05-19 20:15:56
【问题描述】:

当类型本身未知时,我正在尝试找出支持将整数类型(short/int/long)拆箱为其固有类型的语法。

这是一个完全人为的例子来演示这个概念:

 // Just a simple container that returns values as objects
 struct DataStruct
 {
  public short ShortVale;
  public int IntValue;
  public long LongValue;
  public object GetBoxedShortValue() { return ShortVale; }
  public object GetBoxedIntValue() { return IntValue; }
  public object GetBoxedLongValue() { return LongValue; }
 }

 static void Main( string[] args )
 {

  DataStruct data;

  // Initialize data - any value will do
  data.LongValue = data.IntValue = data.ShortVale = 42;

  DataStruct newData;

  // This works if you know the type you are expecting!
  newData.ShortVale = (short)data.GetBoxedShortValue();
  newData.IntValue = (int)data.GetBoxedIntValue();
  newData.LongValue = (long)data.GetBoxedLongValue();

  // But what about when you don't know?
  newData.ShortVale = data.GetBoxedShortValue(); // error
  newData.IntValue = data.GetBoxedIntValue(); // error
  newData.LongValue = data.GetBoxedLongValue(); // error
 }

在每种情况下,整数类型都是一致的,所以应该有某种形式的语法说“对象包含一个简单类型的 X,将其返回为 X(即使我不知道 X 是什么)” .因为对象最终来自同一个来源,所以真的不可能有不匹配的情况(短!=长)。

我为这个人为的例子道歉,这似乎是演示语法的最佳方式。

谢谢。

【问题讨论】:

  • 你们所有的GetBoxed 方法都返回LongValue。错字?
  • “不能有不匹配”是什么意思?如果您知道类型,则不可能;如果你不这样做,那么可能会有。
  • 您希望如何使用拆箱的结果?如果您在拆箱后不知道类型,那么您将无法对它做任何事情(除了猜测和使用dynamic)。如果您确实知道拆箱后的类型,请拆箱到该类型。在您的示例中,您知道 newData.IntValue 只能分配一个 int,因此您必须执行newData.IntValue = (int)yourUnknownBoxedValue;。如果此操作失败,则不能将其分配给 newData.IntValue。如果它没有失败,那么你很好。所以我说的是真的:你应该想一个不做作的例子,因为这没有任何意义。
  • 我已经有一段时间没有发布这个了,但我相信这个问题是一个处理 LINQ 生成的类的通用例程。我不记得具体细节,但我相信密钥被传递给装在对象内的辅助例程;如果帮助者可以简单地“知道”正在传递的内容(short、int 或 long),它可以安全地将键与同一张表中的其他键进行比较。当项目 dba 决定将某些键切换为 short 时,我不再能够使用单个辅助例程。抱歉,我记不起具体细节了。

标签: c# .net object unboxing inferred-type


【解决方案1】:

嗯,object 本身就是框架知道的最通用的类​​型。 is 是盒装值类型(包括原始值类型)还是其他东西都没有关系;如果您想要更具体,必须进行类型转换,除非您仍处于 object 的“松散类型”世界中(或者,在 C# 4 中,dynamic)。

但是请注意,您可以使用一系列条件来实现您想要的:

object boxedValue = GetBoxedValue();
if (typeof(short) == boxedValue.GetType()) {
  newData.ShortValue = (short)boxedValue;
} else if (typeof(int) == boxedValue.GetType()) {
  newData.IntValue = (int)boxedValue;
} else if (typeof(long) == boxedValue.GetType()) {
  newData.LongValue = (long)boxedValue;
} else {
  // not one of those
}

编辑:一个通用的“盒子”也可以做你想做的事:

public class Box<T>: IConvertible where T: struct, IConvertible {
    public static implicit operator T(Box<T> boxed) {
        return boxed.Value;
    }

    public static explicit operator Box<T>(T value) {
        return new Box<T>(value);
    }

    private readonly T value;

    public Box(T value) {
        this.value = value;
    }

    public T Value {
        get {
            return value;
        }
    }

    public override bool Equals(object obj) {
        Box<T> boxed = obj as Box<T>;
        if (boxed != null) {
            return value.Equals(boxed.Value);
        }
        return value.Equals(obj);
    }

    public override int GetHashCode() {
        return value.GetHashCode();
    }

    public override string ToString() {
        return value.ToString();
    }

    bool IConvertible.ToBoolean(IFormatProvider provider) {
        return value.ToBoolean(provider);
    }

    char IConvertible.ToChar(IFormatProvider provider) {
        return value.ToChar(provider);
    }

    sbyte IConvertible.ToSByte(IFormatProvider provider) {
        return value.ToSByte(provider);
    }

    byte IConvertible.ToByte(IFormatProvider provider) {
        return value.ToByte(provider);
    }

    short IConvertible.ToInt16(IFormatProvider provider) {
        return value.ToInt16(provider);
    }

    ushort IConvertible.ToUInt16(IFormatProvider provider) {
        return value.ToUInt16(provider);
    }

    int IConvertible.ToInt32(IFormatProvider provider) {
        return value.ToInt32(provider);
    }

    uint IConvertible.ToUInt32(IFormatProvider provider) {
        return value.ToUInt32(provider);
    }

    long IConvertible.ToInt64(IFormatProvider provider) {
        return value.ToInt64(provider);
    }

    ulong IConvertible.ToUInt64(IFormatProvider provider) {
        return value.ToUInt64(provider);
    }

    float IConvertible.ToSingle(IFormatProvider provider) {
        return value.ToSingle(provider);
    }

    double IConvertible.ToDouble(IFormatProvider provider) {
        return value.ToDouble(provider);
    }

    decimal IConvertible.ToDecimal(IFormatProvider provider) {
        return value.ToDecimal(provider);
    }

    DateTime IConvertible.ToDateTime(IFormatProvider provider) {
        return value.ToDateTime(provider);
    }

    string IConvertible.ToString(IFormatProvider provider) {
        return value.ToString(provider);
    }

    object IConvertible.ToType(Type conversionType, IFormatProvider provider) {
        return value.ToType(conversionType, provider);
    }
}

然后可以使用它来代替object;它仍然是一个对象引用,但它也是原始结构或原始类型的强类型。

【讨论】:

  • 附带说明,框架已经包含一个与Box&lt;T&gt; 非常相似的类(尽管没有转换运算符和IConvertible):StrongBox&lt;T&gt; (System.Runtime.CompilerServices)。
【解决方案2】:

我不完全确定你想用这个实现什么,但你的 DataStruct 类型是错误的。

我想,并非所有方法都返回 LongValue。

struct DataStruct
{
    public short ShortVale;
    public int IntValue;
    public long LongValue;
    public object GetBoxedShortValue() { return ShortVale; }
    public object GetBoxedIntValue() { return IntValue; }
    public object GetBoxedLongValue() { return LongValue; }
}

否则,您始终可以使用 Convert 类尝试在不同类型之间进行转换。
例如:

Convert.ToInt32(SomeObject);

如果您的意思不同,请澄清您的帖子(只需点击编辑按钮并进行编辑)。

顺便说一句,从object 转换可能很容易出错,因为它是所有内容的基本类型。因此,object 可以是任何东西,这意味着您不能总是安全地将 object 转换为 int 或任何其他类型。

更多示例:

int value;
try
{
    value = Convert.ToInt32(someObject);
}
catch (FormatException)
{
    // the convertion is unsuccessful
}

这也很有用:

int myValue;
if (!int.TryParse(something, out myValue))
{
    //unsuccessful
}

我希望这会有所帮助。

【讨论】:

  • @Hans,Convert.ToInt32() 要求您在编译时知道 Int 类型——这正是我想要避免的。
【解决方案3】:

您可以返回dynamic,然后可以将其强制转换为整数类型。

【讨论】:

    【解决方案4】:

    正如其他人所说,您的示例不起作用,因为您从每个方法返回 LongValue,因此您将在这里得到一个无效的转换异常(装箱的 long 不能转换为 short)。

    newData.ShortVale = (short)data.GetBoxedShortValue();
    

    但是,使用 C# 4 的 dynamic,这将起作用(注意对 GetBoxed 方法和 dynamic 而不是 object 的修复:

    // Just a simple container that returns values as objects
    struct DataStruct
    {
        public short ShortVale;
        public int IntValue;
        public long LongValue;
        public dynamic GetBoxedShortValue() { return ShortValue; }
        public dynamic GetBoxedIntValue() { return IntValue; }
        public dynamic GetBoxedLongValue() { return LongValue; }
    }
    
    static void Main( string[] args )
    {
        DataStruct data;
    
        // Initialize data - any value will do
        data.LongValue = data.IntValue = data.ShortVale = 42;
    
        DataStruct newData;
    
        newData.ShortVale = (short)data.GetBoxedShortValue();
        newData.IntValue = (int)data.GetBoxedIntValue();
        newData.LongValue = (long)data.GetBoxedLongValue();
    
        newData.ShortVale = data.GetBoxedShortValue(); // ok
        newData.IntValue = data.GetBoxedIntValue(); // ok
        newData.LongValue = data.GetBoxedLongValue(); // ok
    }
    

    请注意,在最后三种情况下您不需要任何强制转换。但是,还要注意,如果类型不对齐,就像在GetBoxedShortValue() { return LongValue; } 中一样,最后三行将导致无效的强制转换异常。 (有趣的是,前三个不会,它们会正常工作,但是当您将 dynamic 改回 object 时,它们会抛出无效的强制转换异常。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-04
      • 1970-01-01
      • 2022-11-03
      • 1970-01-01
      • 2021-10-07
      • 2021-09-18
      相关资源
      最近更新 更多