您必须指定类型,但不必在q.Return<E,D>()
. 有一些方法可以传递指定类型参数,以便可以隐式推断。为此,您需要稍微更改签名。
public static IQueryable<D> ReturnDTO<E, D>(this IQueryable<E> query, D dtoTypeExample = default(D))
where D : BaseDTO, new()
where E : BaseObjectWithDTO<D, int>
{
//expression tree code to convert
}
现在,即使有一个默认参数,除非您传入一些参数,否则编译器将无法获取它。但是,您传入的内容不必由该方法以任何其他方式使用。例如,假设您有:
public class ProductDTO : BaseDTO {
public static ProductDTO Empty { get { return new ProductDTO(); } }
}
public class Product : BaseObjectWithDTO<ProductDTO,int> {
public static IQueryable<Product> QuerySource { get; set; }
}
然后你可以打电话:
ProductDTO dto = Product.QuerySource.ReturnDTO(ProductDTO.Empty);
我并不是说这一定是个好主意,但你可以做到。此外,它不一定是您传入的实际类型 - 您只需要传入足够接近编译器推断预期类型的东西。例如,您可以有如下签名:
public static IQueryable<D> ReturnDTO<E, D>(this IQueryable<E> query, Func<D,D> dtoIdentity = default(Func<D,D>))
where D : BaseDTO, new()
where E : BaseObjectWithDTO<D, int>
{
//expression tree code to convert
}
那么如果你有:
public class ProductDTO : BaseDTO {
public static ProductDTO Identity(ProductDTO dto){ return dto; };
}
public class Product : BaseObjectWithDTO<ProductDTO,int> {
public static IQueryable<Product> QuerySource { get; set; }
}
然后你可以打电话:
ProductDTO dto = Product.QuerySource.ReturnDTO(ProductDTO.Identity);
这对某些人来说可能更具语义意义,但它有点主观。再一次,我不推荐这个,只是说你可以做到。如果你决定这样做,它可能会为你节省一些工作来拥有一个自引用的泛型基础(警告: Eric Lippert 不鼓励这种事情)。但无论如何,您的设计将如下所示:
public abstract class BaseDTO<T> where T : BaseDTO<T>, new()
{
public static T Empty { get { return new T(); } }
}
public class ProductDTO : BaseDTO<ProductDTO> { }
ReturnDTO
如果您想强制执行一个不变量,即所有 DTO 都是BaseDTO<T>
具有公共无参数构造函数的自引用衍生物,您也可以将类型约束添加到您的方法中。但是,如果您尝试编写通常被认为是好的代码,您可能不会这样做,如果您认为它很难看,您只需闭上眼睛并明确使用参数约束。
我想到了另一件事,不会如此不赞成。想想Queryable.Cast<T>
和Queryable.OfType<T>
方法。它们采用非通用IQueryable
参数,但返回一个IQueryable<T>
. 如果您确保验证您对参数的假设,它可能已经足够干净了。然后你会失去一些编译时类型安全。你需要有一个非通用的基础BaseObjectWithDTO
,就像BaseObjectWithDTO<TData,TKey>
继承自。您的方法将如下所示:
public static IQueryable<D> ReturnDTO<D>(this IQueryable<BaseObjectWithDTO> query)
where D : BaseDTO, new()
{
if(query == null) throw new ArgumentNullException("query");
if( !typeof(BaseObjectWithDTO<D,int>) .IsAssignableFrom(query.GetType().GetGenericParameters()[0]))
throw new ArgumentOutOfRangeException("query");
//expression tree code to convert
}
这并不可怕。但也未必好。它可能比我列出的其他选项更好,但谁知道呢。
我刚刚想到另一种可能对您有用的语法,但它也很滥用。想象一下你确实走了这BaseDTO<T> where T : BaseDTO<T>,new()
条路。您可以在该类型上声明该方法以提取可查询的 DTO。这就是我的想法:
public abstract class BaseDTO<T>
where T : BaseDTO<T>, new()
{
public static T From(BaseObjectWithDTO<T,int> entity){
if(entity == null) throw new ArgumentNullException("entity");
//expression tree code to convert
}
}
那么您就不再需要将该方法ReturnDTO
作为扩展方法了,因为您拥有普通的 LINQ。如果需要,您仍然可以将其添加为语法糖,但是使用这些语义,您的调用最终看起来像:
IQueryable<ProductDTO> dtoQuery = from entity in Product.QuerySource select ProductDTO.From(entity);
也可以写成
Product.QuerySource.Select(entity => ProductDTO.From(entity));
如果您使用的是 anIEnumerable
而不是 anIQueryable
可能是
Product.QuerySource.Select(ProductDTO.From);
请记住:我所说的只是你可以这样做。我不是说你应该。