在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.");
}
}
}
答案 0 :(得分:5)
这称为拳击,在性能和内存使用方面,成本实际上都很高。如果你想避免装箱,你想让一些简单的类型如int和double共享内存,使用LayoutKind struct layout attribute强制它们共享内存,然后将列表设置为该结构类型。例如:
[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;
}
编辑:您可以创建上述数组,而无需装箱以使用这些值填充数组成员。但是,您无法将数组添加到结构中,并使其与非CLI引用类型共享内存。
答案 1 :(得分:1)
需要付费。将int存储为对象时,需要装箱。值类型直接存储,而引用类型(对象是)存储在为其分配的自己的内存中(稍后由垃圾收集器释放)。将值类型分配给对象时,它将装箱成为在托管堆上分配的对象。当退回时,它是 unboxed 。
根据您的需要,拳击的费用可能相关或不相关。除非您使用大量数据进行科学计算,具有大量对象和高帧率要求等的游戏。我认为成本不相关。在这种情况下,最好只有在发现性能问题时才能寻求易于维护和优化的可读代码。
答案 2 :(得分:1)
所以我非常喜欢AresAvatar的答案,除了使用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; }
}
}