C#中的运行时强制转换?

C#中的运行时强制转换?,c#,generics,runtime,casting,C#,Generics,Runtime,Casting,我从一种自定义数据格式中读取数据,该格式在概念上将数据存储在表中。每个列都可以有不同的类型。这些类型特定于文件格式,并映射到C#类型 我有一个列类型,它封装了列的概念,通用参数T表示列中的C#type。Column.FormatType根据格式类型指示类型。因此,要读取列的值,我有一个简单的方法: protected T readColumnValue<T>(Column<T> column) { switch (column.FormatType) {

我从一种自定义数据格式中读取数据,该格式在概念上将数据存储在表中。每个列都可以有不同的类型。这些类型特定于文件格式,并映射到C#类型

我有一个列类型,它封装了列的概念,通用参数T表示列中的C#type。Column.FormatType根据格式类型指示类型。因此,要读取列的值,我有一个简单的方法:

protected T readColumnValue<T>(Column<T> column)
{
  switch (column.FormatType)
  {
    case FormatType.Int:
      return (T)readInt();
  }
}
太好了,所以我得到了我要返回的值类型。我能用它做什么?如果这是Java,因为存在一个执行运行时强制转换的Class.Cast方法,那么我就没有家了!(因为每个Java类都有一个泛型类型参数,指示类的类型是for,它还将提供编译时类型安全性。)以下内容来自我的梦想世界,其中C#type类与Java类类似:

protected T readColumnValue<T>(Column<T> column)
{
  Type<T> valueType=typeof(T);
  switch (column.FormatType)
  {
    case FormatType.Int:
      return valueType.Cast(readInt());
  }
}
protectedt readColumnValue(列)
{
类型值类型=类型(T);
开关(column.FormatType)
{
case FormatType.Int:
返回valueType.Cast(readInt());
}
}
显然没有Type.Cast()---那我该怎么办呢

(是的,我知道有一个Convert.ChangeType()方法,但它似乎执行转换,而不是简单的强制转换。)

更新:如果不使用(T)(object)readInt()进行装箱/拆箱,这似乎是不可能的。但这是不可接受的。这些文件非常大,例如80MB。假设我要读取整列值。我有一个优雅的小方法,它使用泛型并像这样调用上面的方法:

public T[] readColumn<T>(Column<T> column, int rowStart, int rowEnd, T[] values)
{
  ...  //seek to column start
  for (int row = rowStart; row < rowEnd; ++row)
  {
    values[row - rowStart] = readColumnValue(column);
    ... //seek to next row
public abstract class Column<T>
{
  public abstract T readValue(MyParser myParser);
}

public class IntColumn : Column<int>
{
  public override int readValue(MyParser myParser)
  {
    return myParser.readInt();
  }
}
public T[]readColumn(列列、int-rowStart、int-rowEnd、T[]值)
{
…//查找列的起始位置
对于(int row=rowStart;row
对数百万个值进行装箱/拆箱?听起来不太好。我觉得我不得不抛弃泛型,求助于readColumnInt()、readColumnFloat()等,并复制所有这些代码以防止装箱/拆箱是荒谬的

public int[] readColumnInt(Column<int> column, int rowStart, int rowEnd, int[] values)
{
  ...  //seek to column start
  for (int row = rowStart; row < rowEnd; ++row)
  {
    values[row - rowStart] = readInt();
    ... //seek to next row

public float[] readColumnFloat(Column<float> column, int rowStart, int rowEnd, float[] values)
{
  ...  //seek to column start
  for (int row = rowStart; row < rowEnd; ++row)
  {
    values[row - rowStart] = readFloat();
    ... //seek to next row
public int[]readColumnInt(列列、int行开始、int行结束、int[]值)
{
…//查找列的起始位置
对于(int row=rowStart;row

这太可怜了。(

我认为实现这一点的最接近的方法是重载readColumnInfo,而不是像这样使其通用:

    protected Int32 readColumnValue(Column<Int32> column) {
        return readInt();
    }
    protected Int64 readColumnValue(Column<Int64> column) {
        return readLong();
    }
    protected String readColumnValue(Column<String> column){
        return String.Empty;
    }
受保护的Int32 readColumnValue(列){
返回readInt();
}
受保护的Int64 readColumnValue(列){
返回readLong();
}
受保护的字符串readColumnValue(列){
返回字符串。空;
}

所有这些问题的简短答案(请参见问题细节)是,C#不允许显式转换为泛型类型T,即使您知道T的类型,并且您知道您拥有的值是T,除非您想接受装箱/取消装箱:

return (T)(object)myvalue;
就个人而言,这似乎是语言中的一个主要缺陷——没有任何情况表明需要进行装箱/拆箱

但是,如果您提前知道所有可能的不同类型的T,那么还有一个解决方法。继续问题中的示例,我们有一个表示文件中表格数据的通用类型T列,以及一个根据列的类型从列中读取值的解析器。我希望在解析器中包含以下内容:

protected T readColumnValue<T>(Column<T> column)
{
  switch (column.FormatType)
  {
    case FormatType.Int:
      return (T)readInt();
  }
}

因此,如果编译器能够在方法专门化中找出如何强制转换为T,那么它应该能够在一行强制转换中找到它。换句话说,没有任何东西可以阻止C#拥有一个typeof(T).cast()方法,该方法将执行与上述方法专门化中完全相同的操作

(整个练习中更令人沮丧的是,这个解决方案迫使我将解析代码混合到数据对象模型中,而此前我一直在努力将其分开。)


现在,如果有人编译了它,查看生成的CIL,发现.NET正在装箱/取消装箱返回值,以便专门的readValue()方法能够满足通用返回类型T,我会哭。

为什么不实现自己的强制转换操作符,从
T

public class Column<T>
{
    public static explicit operator T(Column<T> value)
    {
        return value;
    }

    private T value;
}
公共类列
{
公共静态显式运算符T(列值)
{
返回值;
}
私人T值;
}
然后,您可以在需要时轻松转换:

Column<int> column = new Column<int>(...)
int value = (int)column;
Column Column=新列(…)
int值=(int)列;

数据是按行主顺序还是按列主顺序存储的?如果数据是按行主顺序存储的,则必须多次扫描整个数据集(您所说的数百万个值)才能挑出每一列,这将使装箱成本相形见绌

我真的建议在数据的一次传递中完成所有工作,可能是通过构建一个
操作
(或
谓词
以报告错误)委托向量,将单个单元格处理成与列关联的
列表。关闭的委托可能会有很大帮助。例如:

public class TableParser
{
    private static bool Store(List<string> lst, string cell) { lst.Append(cell); return true; }
    private static bool Store(List<int> lst, string cell) { int val; if (!int.TryParse(cell, out val)) return false; lst.Append(val); return true; }
    private static bool Store(List<double> lst, string cell) { double val; if (!double.TryParse(cell, out val)) return false; lst.Append(val); return true; }
    private static readonly Dictionary<Type, System.Reflection.MethodInfo> storeMap = new Dictionary<Type, System.Reflection.MethodInfo>();

    static TableParser()
    {
        System.Reflection.MethodInfo[] storeMethods = typeof(TableParser).GetMethods("Store", BindingFlags.Private | BindingFlags.Static);
        foreach (System.Reflection.MethodInfo mi in storeMethods)
            storeMap[mi.GetParameters()[0].GetGenericParameters()[0]] = mi;
    }

    private readonly List< Predicate<string> > columnHandlers = new List< Predicate<string> >;

    public bool TryBindColumn<T>(List<T> lst)
    {
        System.Reflection.MethodInfo storeImpl;
        if (!storeMap.TryGetValue(typeof(T), out storeImpl)) return false;
        columnHandlers.Add(Delegate.Create(typeof(Predicate<string>), storeImpl, lst));
        return true;
    }

    // adapt your existing logic to grab a row, pull it apart with string.Split or whatever, and walk through columnHandlers passing in each of the pieces
}
公共类表解析器
{
私有静态布尔存储(列表lst,字符串单元格){lst.Append(单元格);返回true;}
私有静态布尔存储(List lst,string cell){int val;if(!int.TryParse(cell,out val))返回false;lst.Append(val);返回true;}
私有静态bool存储(List lst,string cell){double val;if(!double.TryParse(cell,out val))返回false;lst.Append(val);返回true;}
私有静态只读字典storeMap=new Dictionary();
静态表解析器()
{
System.Reflection.MethodInfo[]storeMe
protected T readColumnValue<T>(Column<T> column)
{
  return column.readValue(this);
}
  public abstract T readValue(MyParser myParser);
  public override int readValue(MyParser myParser)
public class Column<T>
{
    public static explicit operator T(Column<T> value)
    {
        return value;
    }

    private T value;
}
Column<int> column = new Column<int>(...)
int value = (int)column;
public class TableParser
{
    private static bool Store(List<string> lst, string cell) { lst.Append(cell); return true; }
    private static bool Store(List<int> lst, string cell) { int val; if (!int.TryParse(cell, out val)) return false; lst.Append(val); return true; }
    private static bool Store(List<double> lst, string cell) { double val; if (!double.TryParse(cell, out val)) return false; lst.Append(val); return true; }
    private static readonly Dictionary<Type, System.Reflection.MethodInfo> storeMap = new Dictionary<Type, System.Reflection.MethodInfo>();

    static TableParser()
    {
        System.Reflection.MethodInfo[] storeMethods = typeof(TableParser).GetMethods("Store", BindingFlags.Private | BindingFlags.Static);
        foreach (System.Reflection.MethodInfo mi in storeMethods)
            storeMap[mi.GetParameters()[0].GetGenericParameters()[0]] = mi;
    }

    private readonly List< Predicate<string> > columnHandlers = new List< Predicate<string> >;

    public bool TryBindColumn<T>(List<T> lst)
    {
        System.Reflection.MethodInfo storeImpl;
        if (!storeMap.TryGetValue(typeof(T), out storeImpl)) return false;
        columnHandlers.Add(Delegate.Create(typeof(Predicate<string>), storeImpl, lst));
        return true;
    }

    // adapt your existing logic to grab a row, pull it apart with string.Split or whatever, and walk through columnHandlers passing in each of the pieces
}