【问题标题】:Assigning instance variables obtained through reflection in generic method在泛型方法中分配通过反射获得的实例变量
【发布时间】:2020-02-18 15:30:21
【问题描述】:

我在制表符分隔值 (TSV) 文本文件中有数据,我想读取这些数据并(最终)存储在数据库表中。对于 TSV 文件,每行包含一条记录,但在一个文件中,记录可以有 2 个字段,在另一个文件中可以有 4 个字段,等等。我编写了工作代码来处理 2 字段记录,但我认为这可能是一个好的一个(或两个)通用方法的案例,而不是为每种记录编写新方法。但是,由于两个问题,我无法对此进行编码:我无法创建一个新对象来保存记录数据,并且我不知道如何使用反射来一般地填充我的对象的实例变量。

我查看了其他几个类似的帖子,包括Datatable to object by using reflection and linq

下面是有效的代码(如果重要的话,这是在 Windows 中)以及无效的代码。

public class TSVFile
{
    public class TSVRec
    {
        public string item1;
        public string item2;
    }

    private string fileName = "";

    public TSVFile(string _fileName)
    {
        fileName = _fileName;
    }

    public TSVRec GetTSVRec(string Line)
    {
        TSVRec rec = new TSVRec();

        try
        {
            string[] fields = Line.Split(new char[1] { '\t' });

            rec.item1 = fields[0];
            rec.item2 = fields[1];
        }
        catch (Exception ex)
        {
            System.Windows.Forms.MessageBox.Show("Bad import data on line: " + 
                Line + "\n" + ex.Message, "Error",
                System.Windows.Forms.MessageBoxButtons.OK,
                System.Windows.Forms.MessageBoxIcon.Error);
        }

        return rec;
    }

    public List<TSVRec> ImportTSVRec()
    {
        List<TSVRec> loadedData = new List<TSVRec>();
        using (StreamReader sr = File.OpenText(fileName))
        {
            string Line = null;
            while ((Line = sr.ReadLine()) != null)
            {
                loadedData.Add(GetTSVRec(Line));
            }
        }

        return loadedData;
    }

    // *** Attempted generic methods ***
    public T GetRec<T>(string Line)
    {
        T rec = new T();  // compile error!
        Type t = typeof(T);

        FieldInfo[] instanceVars = t.GetFields();

        string[] fields = Line.Split(new char[1] { '\t' });

        for (int i = 0; i < instanceVars.Length - 1; i++)
        {
            rec. ??? = fields[i];   // how do I finish this line???
        }

        return rec;
    }

    public List<T> Import<T>(Type t)
    {
        List<T> loadedData = new List<T>();
        using (StreamReader sr = File.OpenText(fileName))
        {
            string Line = null;
            while ((Line = sr.ReadLine()) != null)
            {
                loadedData.Add(GetRec<T>(Line));
            }
        }

        return loadedData;
    }
}

我看到了这条线 T rec = 新 T(); 在上述帖子中,但它对我不起作用......

如果可能的话,我将不胜感激有关如何完成这项工作的任何建议。我想了解更多关于将反射与泛型结合使用的信息,因此我不仅想了解如何使用,还想了解原因。

【问题讨论】:

  • 编译错误是什么?我猜它说你需要通过添加where T: class 来约束T。因为 T 有可能是不可实例化的东西,所以你会得到一个编译错误。
  • " 但我认为这可能是一个(或两个)通用方法的好案例,而不是为每种记录编写新方法。”泛型当然假设了一些共同特征/行为,这是由一个泛型约束定义的,例如类似where T: MyBaseClass。在我看来,不同类型的不同成员并不符合这一点。
  • 我同意,作为 TSV,使用带有值字符串数组的泛型类更有意义,或者如果您有具体类型,并且您真的想要一种通用的方式来实例化它们(而不使用工厂模式),您可以简单地给他们一个 params string [] 构造函数并使用激活器 createinstance。
  • TSVRec 的值应该有一个List&lt;string&gt;。添加方法以将它们转换为不同的类型。从本质上讲,如果您正在编写System.Data.DataRow,请弄清楚您需要什么,然后只需使用 DataRow 和 DataTable。这里没有问题,泛型非常适合解决,除了可能使用List&lt;T&gt; 来收集编译时已知的某些字段或记录类型。当你的泛型类或方法变成一堆反射时,它就不是真正的泛型了。
  • 泛型非常有用,但它们并不是解决大多数问题的正确方法。

标签: c# generics reflection instance-variables


【解决方案1】:

我希望 @EdPlunkett 将他的建议作为答案而不是评论发布,这样我就可以将其标记为答案...

总结一下:做我想做的事,没有必要“在泛型方法中分配通过反射获得的实例变量”。其实不用泛型方法我也可以有一个泛型的解决方案:

public class GenRec
{
    public List<string> items = new List<string>();
}

public GenRec GetRec(string Line)
{
    GenRec rec = new GenRec();

    try
    {
        string[] fields = Line.Split(new char[1] { '\t' });

        for (int i = 0; i < fields.Length; i++)
            rec.items.Add(fields[i]);
    }
    catch (Exception ex)
    {
        System.Windows.Forms.MessageBox.Show("Bad import data on line: " + Line + "\n" + ex.Message, "Error",
            System.Windows.Forms.MessageBoxButtons.OK,
            System.Windows.Forms.MessageBoxIcon.Error);
    }

    return rec;
}

public List<GenRec> Import()
{
    List<GenRec> loadedData = new List<GenRec>();
    using (StreamReader sr = File.OpenText(fileName))
    {
        string Line = null;

        while ((Line = sr.ReadLine()) != null)
            loadedData.Add(GetRec(Line));
    }

    return loadedData;
}

我刚刚测试了这个,它就像一个魅力!

当然,这并不能帮助我学习如何编写泛型方法或使用反射,但我会接受...

【讨论】:

  • 你明白了。我在这里提出的一个建议是在其声明中初始化itemspublic List&lt;string&gt; items = new List&lt;String&gt;(); 当您使用new 创建一个类时,它应该可以使用了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多