简体   繁体   English

Generic <T>怎么演员?

[英]Generic <T> how cast?

I have a "Product" base class, some other classes "ProductBookDetail","ProductDVDDetail" inherit from this class. 我有一个“Product”基类,其他一些类“ProductBookDetail”,“ProductDVDDetail”继承自这个类。 I use a ProductService class to make operation on these classes. 我使用ProductService类对这些类进行操作。 But, I have to do some check depending of the type (ISBN for Book, languages for DVD). 但是,我必须根据类型(书的ISBN,DVD的语言)进行检查。 I'd like to know the best way to cast "productDetail" value, I receive in SaveOrupdate. 我想知道投射“productDetail”值的最佳方法,我在SaveOrupdate中收到。 I tried GetType() and cast with ( ProductBookDetail)productDetail but that's not work. 我尝试使用GetType()并使用( ProductBookDetail)productDetail但这不起作用。

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> : 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();
        }
    }
}

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: 如果在SaveOrUpdate方法中,你的意思是写一个相当于以下的不断扩展的开关:

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: 使用无约束类型参数T ,您需要首先将其强制转换为object

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

With the as keyword you shouldn't need that extra cast to object . 使用as关键字,您不需要对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 : 但更好的是,如果有一个共同的基类ProductDetail ,用于各种产品细节类,那么将其用作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 . 我认为这样做是为了对类型参数使用更有意义的名称,例如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. 如果你这样做,那么编译器应该让你“转发”到从ProductDetail派生的东西,而不ProductDetail转换为object

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 如果你有非泛型属性(在公共接口契约中指定),那么你应该在接口中声明一个由SaveOrUpdate调用的公共函数来处理这个

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" 公共接口的每个实例(ProductDetailBook,productDetail等)将根据“//我不知道根据类知道类型和访问属性”的要求以不同方式定义此函数。

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 这是没有通用服务的众多原因之一

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. 我将通过声明一个基类方法来帮助修改SaveOrUpdate方法来重构代码,然后让派生类覆盖该方法。 Now when you call the base class method in the generic method, you will get the derived classes implmentation 现在,当您在泛型方法中调用基类方法时,您将获得派生类的实现

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 您需要使用IS运算符

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. 在泛型方法中,您必须使用as关键字进行转换以执行此类转换。 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. 如果你对泛型做了很多工作,请阅读Bill Wagners的“更有效的C#”,以便更清楚地处理这个问题。

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. 此代码更适合OOP。 It much simpler, it more extensible and less error prone. 它更简单,更可扩展,更不容易出错。

PS Don't forget about SOLID PS别忘了SOLID

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 . 然后,您可以在实体的某个界面中定义策略,这会强制它们实现CheckConstraints方法。 In your generic repository you then call CheckConstraints before executing SaveOrUpdate . 在通用存储库中,然后在执行SaveOrUpdate之前调用CheckConstraints

Use: 使用:

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

and similarly for others. 和其他人一样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM