在我当前的项目中,我们解析了从外部供应商处收到的CSV文件。 但是,由于供应商将来会支持XML文件,因此如果管理层决定使用XML格式,我想提供一种简单的方法来更改我们的代码。泛型和超类型
为了做到这一点,我们的'工作者'类应该只引用数据类,而不知道源是CSV或XML文件。 但是,我们确实有一些工具(用于调试和测试),这些工具专门为一个源文件(目前为CSV)编写。
可能这个描述有点不清楚,但我希望下面的例子能够为您提供足够的信息来帮助我。 不相关的功能已从类中删除,类/接口已重命名,整体解决方案已被简化为只是为了示例。 目前,我有以下设置。
数据'基'类(可以是任何真的): 这些类由解析器(之一)返回。他们真正做的唯一事情是包含数据(可以被认为是一种DTO)。
public class Person
{
public string FirstName { ... };
public string LastName { ... };
public int Age { ... };
public Person()
{
}
}
ICsvObject
接口: 一种用于CSV数据对象接口。这里最重要的是LoadFromCsv
方法,因为它会被CsvParser
类
public interface ICsvObject
{
int CsvLineNumber { get; set; }
void LoadFromCsv(IList<string> columns);
}
CSV数据类使用: 这些通常是从数据类继承并实现ICsvObject
接口
public class CsvPerson : Person
{
public int CsvLineNumber { get; set; }
public void LoadFromCsv(IList<string> columns)
{
if (columns.count != 3)
throw new Exception("...");
this.FirstName = columns[0];
this.LastName = columns[1];
this.Age = Convert.ToInt32(columns[2]);
}
}
IParser
接口: 该接口为其他类提供了一种方式,在不知道源文件类型的情况下引用解析器。
public interface IParser<T> where T : new()
{
IList<T> ReadFile(string path);
IList<T> ReadStream(Stream sSource);
IList<T> ReadString(string source);
}
CsvParser
类: 这个类实现了IParser
接口,并提供了解析CSV文件。未来,可能会决定为XML文件提供解析器。
public class CsvParser<CsvObjectType> : IParser<CsvObjectType> where CsvObjectType : new(), ICsvObject
{
public IgnoreBlankLines { get; set; }
public ReadFile(string path)
{
...
}
public ReadStream(string path)
{
...
}
public ReadString(string path)
{
List<CsvObjectType> result = new ...;
For each line in the string
{
// Code to get the columns from the current CSV line
...
CsvObjectType item = new CsvObjectType();
item.CsvLineNumber = currentlinenumber;
item.LoadFromCsv(columns);
result.add(item);
}
return result;
}
}
现在,我已经解释了情况一点,让我们的问题: “工人”类不应该与我们所使用的解析器的类型麻烦。 他们应该从解析器收到的是数据对象列表(例如Person),它们不需要接口提供的额外信息(本例中为CsvLineNumber
以及其他实际情况)。 但是,其他工具应该能够获得额外的信息(调试/测试程序...)。
那么,其实我是想如下:
ParserFactory
类: 此类返回特定数据类型正确的解析程序。 将来切换到XML时,必须创建XML解析器并更改工厂类。 调用工厂方法的所有其他类都应该接收有效的IParser
类,而不是特定的分析程序。
public class ParserFactory
{
//Instance property
...
public IParser<Person> CreatePersonParser()
{
return new CsvParser<CsvPerson>();
}
}
这样做,无论我们使用什么类型的解析器,工作者类都会调用工厂方法。 之后可以调用ParseFile
方法来提供“基本”数据类的列表。 返回一个Csv分析器是可以的(它实现了IParser接口)。 但是不支持通用类型。 返回CsvParser<Person>
对工厂有效,但Person
类不实现ICsvObject
接口,并且由于通用约束,因此不能与CsvParser
一起使用。
返回一个CsvParser类或IParser将需要调用类来知道我们正在使用哪个解析器,所以这不是一个选项。 使用两个通用类型输入(一个用于CsvObject类型,另一个用于返回类型)创建CsvParser类也不起作用,因为其他工具应该能够访问由ICsvObject接口提供的额外信息。
也值得一提。这是一个正在修改的旧项目。它仍然是.NET 2.0。 但是,在回答时,您可能会使用更新的技术(如扩展方法或LINQ方法)。以.NET 2.0和更新的方式回答这个问题将会让你获得更多的kudo :-)
谢谢!
如果您找到一种方法来总结您的核心问题并将其表示在顶端,您可能会在这个非常长的问题上得到更好的参与。 – lance
整个长城的文字,并没有明确的问题。你在问什么? –
实际问题:如何返回一个返回基本数据类(Person,NOT CsvPerson)的IParser类,同时仍然保持创建返回特定数据类(CsvPerson,NOT Person)的CsvParser实例的其他情况的能力。 – Nullius