在我當前的項目中,我們解析了從外部供應商處收到的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