タイプを指定する必要がありますが、 で明示的に行う必要はありません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> { }
すべての DTO がパブリック パラメーターなしのコンストラクターReturnDTO
の自己参照派生物であるという不変条件を適用する場合は、メソッドに型制約を追加することもできます。BaseDTO<T>
しかし、従来は良いコードと見なされていたものを書こうとしている場合、おそらくこれを行うことはなく、醜いと思う場合は目を閉じてパラメーター制約を明示的に使用するだけです。
私が考えたことがもう 1 つあります。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));
IEnumerable
の代わりにを使用していたIQueryable
場合
Product.QuerySource.Select(ProductDTO.From);
覚えておいてください: 私が言っているのは、この方法で物事を行うことができるということだけです。私はあなたがすべきだと言っているわけではありません。