Дженерик <T>как снимали? - PullRequest
6 голосов
/ 16 марта 2010

У меня есть базовый класс "Product", некоторые другие классы "ProductBookDetail", "ProductDVDDetail" наследуются от этого класса. Я использую класс ProductService для выполнения операций над этими классами. Но я должен сделать некоторую проверку в зависимости от типа (ISBN для Книги, языки для DVD). Я хотел бы знать лучший способ приведения значения productDetail, которое я получаю в SaveOrupdate. Я пробовал GetType () и приводил с (ProductBookDetail)productDetail, но это не сработало.

Спасибо,

var productDetail = new ProductDetailBook() { .... };
var service = IoC.Resolve<IProductServiceGeneric<ProductDetailBook>>();
service.SaveOrUpdate(productDetail);

var productDetail = new ProductDetailDVD() { .... };
var service = IoC.Resolve<IProductServiceGeneric<ProductDetailDVD>>();
service.SaveOrUpdate(productDetail);


public class ProductServiceGeneric<T> : IProductServiceGeneric<T>
{
    private readonly ISession _session;
    private readonly IProductRepoGeneric<T> _repo;
    public ProductServiceGeneric()
    {
        _session = UnitOfWork.CurrentSession;
        _repo = IoC.Resolve<IProductRepoGeneric<T>>();
    }
    public void SaveOrUpdate(T productDetail)
    {            
        using (ITransaction tx = _session.BeginTransaction())
        {
            //here i'd like ot know the type and access properties depending of the class
            _repo.SaveOrUpdate(productDetail);
            tx.Commit();
        }
    }
}

Ответы [ 8 ]

3 голосов
/ 16 марта 2010

Я не хочу быть критичным, но эта модель просто мне кажется плохой.

Я слышал, как другие говорили, что если вы используете тип в обобщенном методе, то, скорее всего, вы делаете что-то не так.

Я бы реорганизовал ваш код, объявив метод базового класса, чтобы помочь с методом SaveOrUpdate , затем получил бы производные классы переопределить этот метод. Теперь, когда вы вызываете метод базового класса в универсальном методе, вы получите имплементацию производных классов

3 голосов
/ 16 марта 2010

Nooooo

Если у вас есть неуниверсальные свойства (как указано в контракте общего интерфейса), то у вас должна быть объявлена ​​общая функция в интерфейсе, которая вызывается SaveOrUpdate для обработки этого

Каждый экземпляр общего интерфейса (ProductDetailBook, productDetail и т. Д.) Будет определять эту функцию по-разному в соответствии с требованием "// здесь я хотел бы не знать тип и свойства доступа в зависимости от класса"

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

Это одна из многих причин НЕ иметь общие услуги

3 голосов
/ 16 марта 2010

Если вам нужно знать о полях или свойствах типа, чтобы «сохранить или обновить», вы можете использовать отражение. Таким образом, класс останется действительно общим.

Если в рамках вашего SaveOrUpdate метода вы хотите написать постоянно расширяющийся переключатель, эквивалентный:

if (it's type A) { deal with type A }
else if (it's type B) { deal with type B }
... and so on

Тогда вы делаете это "неправильно". Этот класс не является действительно универсальным в своем параметре типа. Он работает только с указанным вами набором типов. Я говорю «неправильно» в кавычках, потому что в некоторых ситуациях это может быть лучше, чем доступные альтернативы, но это нежелательно. Если у вас есть запасной вариант для всех других типов, поэтому он всегда работает, то это может быть хорошим способом иметь специальные случаи для определенных типов.

Однако вы можете сделать такой тест или кастинг. С параметром неограниченного типа T сначала необходимо привести его к object:

var eitherStringOrNull = (string)((object)somethingOfTypeT);

При использовании ключевого слова as вам не нужно это дополнительное приведение к object.

var eitherStringOrNull = somethingOfTypeT as string;
if (eitherStringOrNull != null)
{
    .. it was a string, so we can use it as such
}

Но еще лучше, если есть общий базовый класс, ProductDetail, для всех видов классов деталей продукта, тогда используйте его как ограничение для T:

public class ProductServiceGeneric<T> : IProductServiceGeneric<T>
       where T : ProductDetail

Я думаю, что при этом рекомендуется использовать более значимое имя для параметра типа, например TProductDetail.

Если вы сделаете это, то компилятор должен позволить вам "привести вниз" к чему-то, производному от ProductDetail, без необходимости сначала приводить к object.

0 голосов
/ 20 января 2011

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

0 голосов
/ 16 марта 2010

Возможно, вам следует изменить код следующим образом:

abstract class Product 
{
   public abstract bool CheckProduct();
}
class ProductBookDetail : Product
{
   public override bool CheckProduct()
   {
      //Here we can check ProductBookDetail
   }
}

class ProductDetailDVD : Product
{
   public override bool CheckProduct()
   {
      //Here we can check ProductDetailDVD
   }
}

public class ProductServiceGeneric<T> : IProductServiceGeneric<T> where T : ProductDetail
{
    public void SaveOrUpdate(T product)
    {
       if (!product.CheckProduct())
       {
           //product checking failes. Add necessary logic here
       }
    }
}

Этот код больше подходит для ООП. Это намного проще, он более расширяемый и менее подвержен ошибкам.

P.S. Не забудьте про S.O.L.I.D.

0 голосов
/ 16 марта 2010

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

Если вы много работаете с дженериками, прочитайте Билла Вагнерса «Более эффективный C #», чтобы найти альтернативные способы решения этой проблемы более чисто.

public void SaveOrUpdate(T productDetail) 
{             
    using (ITransaction tx = _session.BeginTransaction()) 
    { 
        ProductDetailBook bookDetail = productDetail as ProductDetailBook;
        if (bookDetail != null)
           _repo.SaveOrUpdate(bookDetail); 
        tx.Commit(); 
    } 
} 
0 голосов
/ 16 марта 2010

Если я понимаю ваш вопрос, вы пытаетесь определить, какой у вас производный класс, из функции, которая возвращает базовый класс. Вам нужно использовать оператор IS

ниже вы можете увидеть, как пользоваться оператором.

class Base
{
}

class AB : Base
{

}
class AC : Base { }

class Program
{
   static Base GetObject()
   {
       return null;
   }
   static void Main(string[] args)
   {
       Base B = GetObject();
       if (B is AB)
       {
          AB DevClass =(AB) B;
       }
   }


}

}

0 голосов
/ 16 марта 2010

Использование:

if(productDetail is ProductDetailBook)
{
...
...
}

и аналогично для других.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...