c # Дженерики: разные типы возвращаемых данных с использованием дженериков

#c# #generics #interface #return-type

#c# #обобщения #интерфейс #возвращаемый тип

Вопрос:

У меня есть библиотека доступа к данным, которую я хотел бы возвращать в различных форматах данных (XML, Json, DataTable). Я пытался использовать дженерики для достижения этой цели.

 public interface IDBInteractor<T>
{
    T ExecuteDSQuery(string myQuery)
}

public class DBInteractorDT : IDBInteractor<DataTable>
{
    public DataTable ExecuteDSQuery(string myQuery)
    {
        return new DataTable();
    }
}

public class DBInteractorJson : IDBInteractor<JsonString>
{
    public JsonString ExecuteDSQuery(string myQuery)
    {
        return new JsonString();
    }
}
  

У меня возникли проблемы с вызовом правильного метода. В идеале я бы объявил что-то вроде

      SomeClass<DataTable> dt = new SomeClass<DataTable>();
     SomeClass<JsonString> js = new SomeClass<JsonString>();

     DataTable myDT = dt.ExecuteDSQuery(myQuery);
     JsonString myJson = js.ExecuteDSQuery(myQuery);
  

Я не уверен, как объявить SomeClass. Я знаю, что мог бы сделать что-то вроде

     public class SomeClass<T> where T : IDBInteractor <T>
    {
        public T ExecuteQuery(T dtobject, string myQuery)
        {
           return dtobject.ExecuteDSQuery(myQuery);
        }
    }
  

Но я не хочу передавать экземпляр объекта (dtobject) при каждом вызове метода.

Комментарии:

1. Почему DBInteractorJson ‘s ExecuteDSQuery возвращает строку? Он должен возвращать JsonString

2. Сравните SomeClass<DataTable> dt = new SomeClass<DataTable>(); и public class SomeClass<T> where T : IDBInteractor <T> . Но DataTable это не IDBInteractor <T>

Ответ №1:

Что именно вы пытаетесь сделать? Этот код работает нормально:

 using System.Data;

namespace ConsoleApplication7
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            IDBInteractor<DataTable> dt = new SomeClass<DataTable>(new DBInteractorDT());
            IDBInteractor<JsonString> js = new SomeClass<JsonString>(new DBInteractorJson());
            /*
            or you can write 

            IDBInteractor<DataTable> dt = new DBInteractorDT();
            IDBInteractor<JsonString> js = new DBInteractorJson();

             */

            DataTable myDT = dt.ExecuteDSQuery("");
            JsonString myJson = js.ExecuteDSQuery("");
        }
    }

    public interface IDBInteractor<T>
    {
        T ExecuteDSQuery(string myQuery);
    }

    public class DBInteractorDT : IDBInteractor<DataTable>
    {
        #region IDBInteractor<DataTable> Members

        public DataTable ExecuteDSQuery(string myQuery)
        {
            return new DataTable();
        }

        #endregion
    }

    public class DBInteractorJson : IDBInteractor<JsonString>
    {
        #region IDBInteractor<JsonString> Members

        public JsonString ExecuteDSQuery(string myQuery)
        {
            return new JsonString();
        }

        #endregion
    }

    public class SomeClass<T> : IDBInteractor<T>
    {
        private IDBInteractor<T> interactor;

        public SomeClass(IDBInteractor<T> interactor)
        {
            this.interactor = interactor;
        }

        public T ExecuteDSQuery(string myQuery)
        {
            return interactor.ExecuteDSQuery(myQuery);
        }
    }

    public class JsonString
    {
    }
}
  

Комментарии:

1. Вы правы, я изначально ошибочно вызывал объект типа: DBInteractorDT<DataTable> dt = new DBInteractorDT();

Ответ №2:

если ваш универсальный параметр имеет конструктор без параметров, вы можете создать новый объект T();

но вы должны определить, что у вашего T должен быть конструктор без параметров, использующий универсальное ограничение new(), чтобы заставить это:

 public class SomeClass<T> where T : IDBInteractor <T> , new()
  

Ответ №3:

Вы могли бы добавить ограничение new() в свое определение класса, чтобы ваш класс стал:

 public class SomeClass<T> where T : IDBInteractor <T>, new()
{
    public T ExecuteQuery(string myQuery)
    {
       return new T().ExecuteDSQuery(myQuery);
    }
}
  

Затем вы можете выполнить любую настройку в общедоступном конструкторе без параметров.

Ответ №4:

Ваш SomeClass<T> имеет зависимость от IDBInteractor<T> . Вы могли бы добавить new() ограничение, чтобы вы могли создавать экземпляр в SomeClass<T> всякий раз, когда вам это нужно.

Но почему бы не использовать возможности depencency injection? Просто принять IDBInteractor<T> в качестве параметра в конструкторе SomeClass<T> ?

Код

 public class SomeClass<T> where T : IDBInteractor <T>
{
    private IDBInteractor<T> _interactor;

    public SomeClass(IDBInteractor<T> interactor)
    {
        _interactor = interactor;
    }

    public T ExecuteQuery(string myQuery)
    {
       return _interactor.ExecuteDSQuery(myQuery);
    }
}
  

Смотрите также

http://www.theserverside.com/news/1321158/A-beginners-guide-to-Dependency-Injection

Ответ №5:

Я согласен с fix_likes_coding. Если вы хотите создать класс только для выполнения запроса, вы можете вместо этого посмотреть на методы расширения. Возможно, это не совсем вписывается в общую схему вещей, но может помочь, если вы хотите упростить задачу.

 public static class InteractorUtils
{
    public static T ExecuteQuery(this IDBInteractor<T> interactor, string myQuery)
    {
        return interactor.ExecuteDSQuery(myQuery);
    }
}
  

и вы могли бы использовать это следующим образом:

 IDBInteractor<DataTable> dt = new DBInteractorDT();

dt.ExecuteQuery("");