开发者

Generic <T> how cast?

开发者 https://www.devze.com 2022-12-22 21:01 出处:网络
I have a \"Product\" base class, some other classes \"ProductBookDetail\",\"ProductDVDDetail\" inherit from this class. I use a ProductService class to make operation on these classes. But, I have to

I have a "Product" base class, some other classes "ProductBookDetail","ProductDVDDetail" inherit from this class. I use a ProductService class to make operation on these classes. But, I have to do some check depending of the type (ISBN for Book, languages for DVD). I'd like to know the best way to cast "productDetail" value, I receive in SaveOrupdate. I tried GetType() and cast with (ProductBookDetail)productDetail but that's not work.

Thanks,

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> : IProductS开发者_如何学GoerviceGeneric<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();
        }
    }
}


I don't mean to be critical, but that pattern just feels bad to me.

I've heard others say that if you're taking a type in a generic method, then you're most likely doing something wrong.

I would refactor your code by declaring a base class method to help with the SaveOrUpdate method, then have the derived classes override that method. Now when you call the base class method in the generic method, you will get the derived classes implmentation


Nooooo

If you have non generic properties (as specified in common interface contract) then you should have a common function declared in the interface that is called by SaveOrUpdate to handle this

Each instance of the common interface (ProductDetailBook, productDetail etc) will define this function differently as required by "//here i'd like ot know the type and access properties depending of the class"

You are pulling class specific code and putting it into a common function, this is the start of spaghetti code

This is one of the many reasons NOT to have generic services


If you need to know about the fields or properties of the type in order to "save or update", you could use reflection. That way, the class would remain truly generic.

If within your SaveOrUpdate method you mean to write an ever-expanding switch equivalent to:

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

Then you're doing it "wrong". That class is not really generic in its type parameter. It only works with the specific set of types you specified. I say "wrong" in quotes because it might be better than the available alternatives in some situations, but it's undesirable. If you have a fall-back for all other types, so it always works, then it might be an okay way to have special cases for certain types.

However, you can do such a test, or casting. With an unconstrained type parameter, T, you need to cast it to object first:

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

With the as keyword you shouldn't need that extra cast to object.

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

But even better, if there is a common base class, ProductDetail, for all kinds of product detail class, then use that as a constraint on T:

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

I think it's good practise when doing that to use a more meaningful name for the type parameter, such as TProductDetail.

If you do this, then the compiler should let you "cast down" to something derived from ProductDetail, without having to cast to object first.


If I understand your question you are trying to determine what derived class you have from a function that returns a base class. You need to use the IS operator

you can see how to use the operator below.

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;
       }
   }


}

}


Within generic methods, you have to cast with as keyword to do casts like this. There are good reasons why but its a long story...

If you do a lot with generics, read Bill Wagners "More Effective C#" for alternative ways to dealing with this more cleanly.

public void SaveOrUpdate(T productDetail) 
{             
    using (ITransaction tx = _session.BeginTransaction()) 
    { 
        ProductDetailBook bookDetail = productDetail as ProductDetailBook;
        if (bookDetail != null)
           _repo.SaveOrUpdate(bookDetail); 
        tx.Commit(); 
    } 
} 


Maybe you should refactor your code as follows:

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
       }
    }
}

This code is much more appropriate for OOP. It much simpler, it more extensible and less error prone.

P.S. Don't forget about S.O.L.I.D.


I would look up Strategy pattern and maybe use that in conjunction with your generic repository. Then you can define your strategy in some interface for your entities, which forces them to implement some method like CheckConstraints. In your generic repository you then call CheckConstraints before executing SaveOrUpdate.


Use:

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

and similarly for others.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号