C# 将int(或任何值类型)存储为对象并将其转换回int(或值类型)是否有成本?

C# 将int(或任何值类型)存储为对象并将其转换回int(或值类型)是否有成本?,c#,.net,performance,C#,.net,Performance,在C#中,将int(或任何值类型)存储为对象并将其转换回int(或值类型)是否有成本 基本上我需要创建一个内存表。我可以为每种可能的类型创建特定的“列”(因此任何基本值类型,如int、double、string等,以及我希望存储在表中的用户定义引用类型,如Order),或者我可以简单地将所有内容存储为对象,并在访问表时将它们转换回正确的类型 所以我的问题是,哪种方法表现更好,或者两者都是相同的 或者我应该为所有值类型使用特定的“列”,并将所有用户定义的引用类型存储为对象吗 谢谢-下面的示例代码-

在C#中,将int(或任何值类型)存储为对象并将其转换回int(或值类型)是否有成本

基本上我需要创建一个内存表。我可以为每种可能的类型创建特定的“列”(因此任何基本值类型,如int、double、string等,以及我希望存储在表中的用户定义引用类型,如Order),或者我可以简单地将所有内容存储为对象,并在访问表时将它们转换回正确的类型

所以我的问题是,哪种方法表现更好,或者两者都是相同的

或者我应该为所有值类型使用特定的“列”,并将所有用户定义的引用类型存储为对象吗

谢谢-下面的示例代码-静态测试方法显示使用情况

public sealed class Columns
{
    public static void Test()
    {
        Columns cols = new Columns(100);
        cols.SetInt(0,Int_Column,12345);
        int value = cols.GetInt(0,Int_Column);

        cols.SetObject(1,Object_Column,12345);
        int value2 = (int)cols.GetObject(1,Object_Column);
    }

    private const int Int_Column = 0;
    private const int String_Column = 1;
    private const int Object_Column = 2;

    private int[] _intCol;
    private string[] _stringCol;
    private object[] _objCol;

    public Columns(int rowCount)
    {
        _intCol = new int[rowCount];
        _stringCol = new string[rowCount];
        _objCol = new object[rowCount];
    }

    public void SetInt(int rowIndex, int colIndex, int value)
    {
        switch(colIndex)
        {
            case Int_Column:
                _intCol[rowIndex] = value;
                break;
            default:
                throw new Exception("Incorrect column index specified.");
        }
    }

    public int GetInt(int rowIndex, int colIndex)
    {
        switch(colIndex)
        {
            case Int_Column:
                return _intCol[rowIndex];
            default:
                throw new Exception("Incorrect column index specified.");
        }
    }

    public void SetString(int rowIndex, int colIndex, string value)
    {
        switch(colIndex)
        {
            case String_Column:
                _stringCol[rowIndex] = value;
                break;
            default:
                throw new Exception("Incorrect column index specified.");
        }
    }

    public string GetString(int rowIndex, int colIndex)
    {
        switch(colIndex)
        {
            case String_Column:
                return _stringCol[rowIndex];
            default:
                throw new Exception("Incorrect column index specified.");
        }
    }

    public void SetObject(int rowIndex, int colIndex, object value)
    {
        switch(colIndex)
        {
            case Object_Column:
                _objCol[rowIndex] = value;
                break;
            default:
                throw new Exception("Incorrect column index specified.");
        }
    }

    public object GetObject(int rowIndex, int colIndex)
    {
        switch(colIndex)
        {
            case Object_Column:
                return _objCol[rowIndex];
            default:
                throw new Exception("Incorrect column index specified.");
        }
    }
}
这就是所谓的装箱,其成本实际上是巨大的,无论是在性能上还是在内存使用上。如果要避免装箱,并且要使int和double等一些简单类型共享内存,请使用LayoutKind强制它们共享内存,然后将列表设置为该结构类型。例如:

[StructLayout(LayoutKind.Explicit)]
[CLSCompliant(false)]
public struct NumberStackEntry
{
    /// <summary>Type of entry</summary>
    [FieldOffset(0)]
    public EntryTypes entryType;

    /// <summary>Value if double</summary>
    [FieldOffset(4)]
    public double dval;

    /// <summary>Value if ulong</summary>
    [FieldOffset(4)]
    public ulong uval;

    /// <summary>Value if long</summary>
    [FieldOffset(4)]
    public long lval;

    /// <summary>Value if integer</summary>
    [FieldOffset(4)]
    public int ival;
}
[StructLayout(LayoutKind.Explicit)]
[CLSCompliant(false)]
公共结构NumberStackEntry
{
///条目类型
[字段偏移量(0)]
公共入口类型入口类型;
///如果值为双精度
[现场偏移(4)]
公共双dval;
///如果是ulong,则为Value
[现场偏移(4)]
乌隆乌瓦尔公墓;
///长时的值
[现场偏移(4)]
公共长叶;
///整数值
[现场偏移(4)]
公共国际竞争;
}

编辑:您可以创建上述内容的数组,而无需装箱以使用这些值填充数组成员。但是,不能将数组添加到结构中,并使其与非CLI引用类型共享内存。

这是有代价的。将int存储为对象时,需要将其装箱。值类型直接存储,而引用类型(对象是)存储在分配给它的内存中(稍后由垃圾收集器释放)。将值类型分配给对象时,会将其装箱到对象中,并在托管堆上进行分配。当倒扣时,它是不固定的


根据您的需要,拳击的费用可能相关,也可能无关。除非你用大量的数据量进行科学计算,游戏中有大量的对象和高帧速率的要求等等,否则我认为成本是不相关的。在这种情况下,最好是在注意到性能问题的情况下,努力获得易于维护和优化的可读代码。

因此我非常喜欢阿瑞斯·阿凡达的答案,除了使用LayoutExplicit

警告,结构中的重叠字段用于封送非托管API。即使在那里,它也不能很好地工作,但在某些平台上的某些使用会损坏CLR运行时

以他的例子来说,你可以用这样的方式:

public struct RecordValue
{
    private object _ref;
    private long _val;

    public string String
    {
        get { return _ref as string; }
        set { _ref = value; }
    }

    public double Double 
    { 
        get { return BitConverter.Int64BitsToDouble(_val); }
        set { _val = BitConverter.DoubleToInt64Bits(value); }
    }

    public long Int64
    {
        get { return _val; }
        set { _val = value; }
    }

    public ulong UInt64
    {
        get { return unchecked((ulong)_val); }
        set { _val = unchecked((long)value); }
    }
    public int Int32
    {
        get { return unchecked((int)_val); }
        set { _val = value; }
    }
}

是的,这是有代价的,强制转换到对象会导致一次内存分配。搜索
装箱
取消装箱
。(顺便说一句,字符串不是值类型,因此在强制转换到对象时不会被装箱)因此,如果我存储值类型,例如int数组中的int,它们将不会被正确装箱?不,在这种情况下,它们不会被装箱。注:如果我存储值类型,例如int数组中的int,它们将不会被正确装箱?数组本身是CLI引用类型,而不是值类型,所以不能像上面那样将它们与FieldOffset一起使用。但是,您可以创建上面定义的NumberStackEntry数组,而不必对数组的任何成员进行装箱,是的。使用它我从来没有遇到过任何问题,我使用它除了编组API之外,还用于其他用途。在哪里可以找到它破坏CLR运行时的证据?重叠的整数、长数和字节[]。在调试版本和在x86中运行的发布版本中工作良好;然而,在64位中,它会爆炸。这方面的另一个问题是重叠bool类型,您可以获得一个布尔值,其中(b!=true&&b!=false)将导致一个true表达式。编译器不允许您将诸如byte[]或string之类的CLR类型与其他类型重叠。我说过只对简单类型使用它。我没有看到32位或64位bools的问题。@AresAvatar,这方面的几个主题:@AresAvatar,你说得最好--“如果你超越简单类型,显然会给自己带来麻烦”我必须承认,我对Int16、Int32、Int64或Double没有任何问题,所以也许你的示例工作得很好。由于遇到各种各样的“怪癖”,我只是变得不喜欢使用重叠结构。无论如何,阿瑞斯,我仍然喜欢你的答案,不管你如何使用结构。