2016-06-15 2 views
1

Я хочу создать систему классов для обобщения получения и публикации данных из некоторого источника данных. У меня есть два метода: GetData и PostData. Оба метода должны иметь какой-то ввод, а GetData также должен иметь тип возврата. Я попытался написать общий интерфейс и реализовать его в классе «DatabaseSource»:Общая реализация интерфейса: общий тип возвращаемого типа и общий тип ввода

public class QueryParameter 
{ 
    public QueryParameter() 
    { 
     this.Direction = ParameterDirection.Input; 
    } 

    public string Name { get; set; } 
    public object Value { get; set; } 
    public ParameterDirection Direction { get; set; } 
} 

public class InputBase 
{ 
    public InputBase() 
    { 
     ResultMapping = new Dictionary<string, string>(); 
     Parameters = new List<QueryParameter>(); 
    } 
    public Dictionary<string, string> ResultMapping { get; set; } 
    public List<QueryParameter> Parameters { get; set; } 
} 
public class DatabaseInput: InputBase 
    { 
     public string Query { get; set; } 
     public DatabaseCommandType CommandType { get; set; } 
    } 

public interface IDataSource<I> where I: InputBase 
{ 
    IEnumerable<T> GetData<T>(I input); 
    void PostData(I Input); 
} 

Теперь я пытался реализовать интерфейс, как это:

public class DatabaseDataSource: IDataSource<DatabaseInput> 
{ 
    public IEnumerable<T> GetData<T>(DatabaseInput Input) 
    { 
     //implementation 
    } 

    public void PostData(DatabaseInput Input) 
    { 
     //implementation 
    } 
} 

Но у меня есть проблема, когда я пытаюсь создать экземпляр данных источник, как это:

IDataSource<InputBase> dataSource = new DatabaseDataSource(); 

Я не могу использовать DatabaseInput, потому что этот код в какой-то заводским способом, и я должен быть в состоянии создать экземпляр других реализаций IDataSource.

Вкратце я хочу иметь входные и выходные данные как общие типы и ограничивать входные данные для соответствия конкретной реализации IDataSource.

+0

Вы можете сделать это с помощью оператора http://stackoverflow.com/questions/1096568/how-can-i-use-interface-as-ac-sharp-generic-type-constraint –

+0

Я пробовал использовать, где оператор , но все равно не может быть экземпляром, как в последнем разделе кода в моем вопросе – vpetrovic

+0

'DatabaseDataSource' является' IDataSource 'не' IDataSource '. Вы не можете делать ковариацию с конкретными типами. См. Https://msdn.microsoft.com/en-gb/library/dd799517(v=vs.110).aspx – TheInnerLight

ответ

0

Вы можете использовать ограничение, нажмите here для получения дополнительной информации об ограничениях.

where T : class //The type argument must be a reference type; this applies also to any class, interface, delegate, or array type. 

where T : <interface name> //The type argument must be or implement the specified interface. Multiple interface constraints can be specified. The constraining interface can also be generic. 
+0

Он уже пытается это, это просто семантика, которая неверна. –

+0

Я знаю, где ограничение, но я не могу его построить. Мне нужен конкретный пример с этим, я надеюсь, что я описал свою проблему в своем вопросе, и, как вы можете видеть, я попытался использовать «где» в определении inteface. – vpetrovic

3

Если я правильно понять это (и это правда, не 100% ясно для меня), источник данных необходимо определить и входные и выход. Если вы сделаете это:

public IEnumerable<T> GetData<T>(DatabaseInput Input) 

Тогда фактическая реализация этого метода может существенно варьироваться в зависимости от того, что T есть. Вам не нужен сценарий этого метода, в котором вы проверяете тип T и соответствующим образом разветвляете свой код.

Возможно, что вы хотите что-то вроде этого:

public interface IDataSource<TInput, TOutput> where TInput: InputBase 
{ 
    IEnumerable<TOutput> GetData(TOutput input); 
    void PostData(TInput Input); 
} 

Но даже тогда у вас есть интерфейс, который, образующая два, казалось бы, несвязанные операций. (. Это кажется маловероятным, что TInput может быть использован как в качестве запроса для извлечения данных, и в качестве команды размещать/изменять данные)

Так что, возможно, вы могли бы разбить его дальше:

public interface IDataSource<TInput, TOutput> where TInput: InputBase 
{ 
    IEnumerable<TOutput> GetData(TOutput input); 
} 

public interface IDataCommand<TInput> where TInput:InputBase 
{ 
    void PostData(TInput Input); 
} 

Чтобы создать его экземпляр вы могли бы использовать абстрактную фабрику:

public interface IDataSourceFactory<TInput, TOutput> 
{ 
    IDataSource<TInput, TOutput> Create(); 
    void Release(IDataSource<TInput, TOutput> created); 
} 

причина этого в том, что она позволяет избежать необходимости в ваш класс называть var dataSource = new [whatever]. Если вы сделаете это, то это несколько поражения цели реализации интерфейса. Независимо от того, какой интерфейс вы реализуете, как только вы явно вызываете new и создаете определенный тип, ваш класс связан с , который вводит, а не интерфейс.

Это перемещает оригинальную проблему. Какова реализация абстрактной фабрики? Хорошей новостью является то, что класс, который зависит от фабрики, не волнует, что такое реализация фабрики. Но вам все равно понадобится.Один из способов сделать это - использовать контейнер DI. Виндзор полезен, поскольку он обеспечивает шаблон для создания абстрактных фабрик. This blog post описывает, как это сделать более подробно.

+0

Скотт, я прочитаю ваш ответ более подробно. Я просто хочу уточнить, что такое InputBase: это базовый класс для предоставления параметров, используемых для фильтрации данных (для GetData) или параметров для некоторого запроса на обновление (для PostData). А что касается TResult - там я хочу использовать отражение для загрузки данных в объектах TResult. – vpetrovic

+0

Вот что я подумал. Если вы объявляете «открытый интерфейс IDataSource , где I: InputBase', и этот метод имеет как« get », так и« post », оба принимают« I »в качестве параметра, то это означает, что точно такое же« I »(реализация 'InputBase') должны использоваться как как параметр запроса, так и как параметр update/insert/delete. Но на практике параметр запроса будет другим. Например, если сущность имеет дату, тогда запрос может включать параметры начальной и конечной даты, тогда как команда вставки или обновления будет иметь фактическую дату, а не диапазон запросов. –

Смежные вопросы