【问题标题】:Cast a struct to a class inherits from the same Interface将结构强制转换为从同一接口继承的类
【发布时间】:2015-03-12 11:44:02
【问题描述】:

向同事提出挑战,是否有任何方法可以将结构转换为类,这里是我们尝试过的示例

 namespace ConsoleApplication22
{
    class Program
    {
        static void Main(string[] args)
        {

            IFoo fooS = new FooStruct();
            fooS.Val = 5; 

            FooClass foo =(FooClass) fooS;

        }
    }
    public interface IFoo
    {

        int Val { get; set; }
    }
    public struct FooStruct : IFoo
    {
        public int Val { get; set; }
    }
    public class FooClass : IFoo
    {
        //public FooClass(int val)
        //{

        //}
        private int val;
        public int Val
        {
            get { return val; }
            set { val = value; }
        }
    }
}

但我们得到了一个无效的强制转换异常:D
鉴于接口是引用类型而类是引用类型并且类实现了接口,是否有任何棘手的方法可以提取接口并将其分配给类

【问题讨论】:

  • 强制转换在编译时和运行时通过类型检查进行验证。旧的“通过接口神奇地将一种类型转换为另一种”路线已经很好地涵盖了,并且与 structclass 类型没有什么关系。
  • 在你关闭问题之前等待我认为它已经足够清楚了
  • @AdamHouldsworth 你的意思是在这种情况下不可能进行铸造
  • 答案也很明确。这是不可能的,因为除非您实现了自定义转换运算符(如果有记忆,它不适用于接口),否则无法从类型转换为完全不相关的类型。转换的方法有很多,但转换意味着涉及类型层次结构。
  • 是的。就像您无法成功地将 int 转换为 string 一样。您也许可以将其 看起来 像使用 implicitexplicit 转换运算符应用于其中一种类型的转换。

标签: c# inheritance struct interface casting


【解决方案1】:

我能想到的最hackish的方式(你永远不应该使用的方式!)是通过使用Marshal

public unsafe static TDest UnsafeCast<TDest, TSrc>(object source)
{
    byte[] buffer = new byte[Marshal.SizeOf(typeof(TSrc))];

    fixed (byte* b = buffer)
        Marshal.StructureToPtr(source, new IntPtr(b), true);

    fixed (byte* b = buffer)
        return (TDest) Marshal.PtrToStructure(new IntPtr(b), typeof (TDest));
}

这实际上将FooClass 中保存的数据编组为FooStruct,这允许将其从引用类型“转换”为值类型。

如果您使用FooStruct 类型而不是泛型类型参数,则可以选择跳过第二个封送处理,方法是将缓冲区直接从缓冲区转换为FooStruct 类型:

fixed (byte* b = buffer)
{
    var s = (FooStruct*) b;
    return *s;
}

需要unsafe 编译器选项,并且应该永远 永远在任何类型的生产环境中完成 - 它非常缓慢且不可预测。

【讨论】:

  • 从未见过这样的做法,但它仍然是转换,而不是演员。
【解决方案2】:

您不能通过接口直接在两者之间进行转换,这是不允许的,因为它们彼此不直接相关(即继承)。

编译器可以捕获很多这种类型的东西,甚至不允许强制转换编译。如果编译器无法做到这一点,则运行时会做到这一点。对于显式转换,运行时将在转换失败时抛出异常,对于隐式转换(仅限引用类型)的尝试,运行时返回 null 并且不会抛出异常。

类型检查实际上检查FooStruct 是一个 FooClass,它不可能是。

但是,您可以使用强制转换运算符在它们之间转换,使其看起来像强制转换

class Program
{
    static void Main(string[] args)
    {
        FooStruct f = new FooStruct();
        f.Val = 2;

        FooClass f2 = (FooClass)f;

        Console.Read();
    }
}

class FooClass : IFoo
{
    public static explicit operator FooClass(FooStruct f)
    {
        FooClass foo = new FooClass();
        foo.Val = f.Val;
        return foo;
    }

    public int Val { get; set; }
}



struct FooStruct : IFoo
{
    public int Val { get; set; }

    public static explicit operator FooStruct(FooClass f)
    {
        FooStruct foo = new FooStruct();
        foo.Val = f.Val;
        return foo;
    }
}


// This interface has little use in this scenario.
interface IFoo
{
    int Val { get; set; }
}

不要将转换与转换混淆。此外,请注意将接口应用于struct 类型,因为可能会发生装箱。

【讨论】:

  • 但是这里不再需要IFoo接口了吗?
  • 您可以使用IFoo 接口定义完全独立类型的公共合约,以便他们可以履行该合约。这种尝试使用对于接口来说是错误的。它们不会让您假装两个不相关的类型可以相互入侵,但它们允许您将这两个独立的类型视为 IFoo,因为它们都实现了它,因此符合合同要求。
【解决方案3】:

没有。

为您的FooClass 创建一个以IFoo 为实例的复制方法,并在那里执行必要的复制。

【讨论】:

    猜你喜欢
    • 2011-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多