현재 나의 프로젝트에서 우리는 외부 공급 업체로부터받은 CSV 파일을 구문 분석합니다. 그러나 공급자가 앞으로 XML 파일을 지원할 것이므로 관리자가 XML 형식을 사용해야한다고 결정하면 코드를 쉽게 변경할 수있는 방법을 제공하고자합니다.일반 및 슈퍼 유형
이 작업을 수행하려면 '작업자'클래스는 소스가 CSV 또는 XML 파일인지 모른 채 데이터 클래스 만 참조해야합니다. 그러나 우리는 하나의 소스 파일 (현재 CSV) 용으로 작성된 도구 (디버깅 및 테스트 용)가 있습니다.
아마도이 설명은 약간 분명하지 않지만 다음 예제가 나를 도와 줄 수있는 충분한 정보를 제공하기를 바랍니다. 관련없는 기능이 클래스에서 제거되었으므로 클래스/인터페이스의 이름이 바뀌었고 전체 솔루션은이 예제를 위해 단순화되었습니다. 현재, 다음과 같은 설정이 있습니다.
데이터 '기본'클래스 (실제로 가능할 수 있음) : 이러한 클래스는 파서 (하나)에 의해 반환됩니다. 그들이 실제로하는 유일한 일은 데이터를 포함합니다 (일종의 DTO로 간주 될 수 있음).
public class Person
{
public string FirstName { ... };
public string LastName { ... };
public int Age { ... };
public Person()
{
}
}
ICsvObject
인터페이스 : 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]);
}
}
을 구현 :이
public interface ICsvObject
{
int CsvLineNumber { get; set; }
void LoadFromCsv(IList<string> columns);
}
CSV 데이터 클래스는 CsvParser
클래스에서 사용되기 때문에 여기서 가장 중요한 것은은 LoadFromCsv
방법 IParser
interface : 이 인터페이스는 다른 클래스가 소스 파일 유형을 모른 채 파서를 참조 할 수있는 방법을 제공합니다.
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)이며
ICsvObject
인터페이스 (이 예제에서는
CsvLineNumber
및 실제 상황의 다른 것들)가 제공하는 추가 정보는 필요하지 않습니다. 그러나 다른 도구는 추가 정보 (디버그/테스트 프로그램 ...)를 얻을 수 있어야합니다.
그럼, 내가 실제로 원하는 것은 다음
ParserFactory
클래스 : 이 클래스는 특정 데이터 유형에 대한 올바른 파서를 반환합니다. 앞으로 XML로 전환 할 때 XML 파서를 만들고 팩토리 클래스를 변경해야합니다. 팩토리 메서드를 호출하는 다른 모든 클래스는 특정 파서가 아닌 유효한 IParser
클래스를 받아야합니다.
public class ParserFactory
{
//Instance property
...
public IParser<Person> CreatePersonParser()
{
return new CsvParser<CsvPerson>();
}
}
이렇게하면 작업자 클래스는 사용중인 파서 유형에 관계없이 팩토리 메서드를 호출합니다. ParseFile
메소드를 호출하여 '기본'데이터 클래스 목록을 제공 할 수 있습니다. Csv 파서를 반환하면 OK입니다 (IParser 인터페이스 구현). 그러나 제네릭 형식은 지원되지 않습니다. CsvParser<Person>
을 반환하는 것은 공장에서 유효하지만 Person
클래스는 ICsvObject
인터페이스를 구현하지 않으며 제네릭 제약으로 인해 CsvParser
과 함께 사용할 수 없습니다.
CsvParser 클래스 나 IParser를 반환하면 호출 클래스가 우리가 사용하고있는 파서를 알 수 없으므로 옵션이 아닙니다. 다른 도구가 ICsvObject 인터페이스에서 제공하는 추가 정보에 액세스 할 수 있어야하므로 두 가지 일반 형식 입력 (CsvObject 형식과 반환 형식 중 하나)을 사용하여 CsvParser 클래스를 만들 수도 있습니다.
또한 언급할만한 가치가 있습니다. 이것은 수정중인 오래된 프로젝트입니다. 아직 .NET 2.0입니다. 그러나 응답 할 때 확장 메서드 나 LINQ 메서드와 같은 최신 기술을 사용할 수 있습니다. .NET 2.0 이상에서 질문에 대답하면 훨씬 더 많은 kudo를 얻을 수 있습니다 :-)
고마워!
핵심 질문을 요약하고 맨 위에 표시하는 방법을 찾으면이 매우 긴 질문에 더 적극적으로 참여할 수 있습니다. – lance
텍스트의 전체 위대한 벽과 명확한 질문이 없습니다. 네가 묻는 것은 무엇인가? –
실제 질문 : 특정 데이터 클래스 (CsvPerson, NOT Person)를 반환하는 CsvParser 인스턴스가 생성 된 다른 상황에서도 여전히 기본 데이터 클래스 (Person, NOT CsvPerson)를 반환하는 IParser 클래스를 반환 할 수 있습니까? – Nullius