打开通用约束

时间:2015-10-12 05:09:14

标签: c# generics

给出以下界面:

public interface IQuerySpec<M> { }

我很乐意制作这样的扩展方法:

public static OrderedSortation<T> OrderBy<T, TKey>(
    this T query, 
    Expression<Func<T, TKey>> sort) 
where T : IQuerySpec<?> {
    //business as usual
}

我只是想确保T类型是IQuerySpec<M>的某种变体。然后,我可以按如下方式调用扩展方法:

public class Foo : IQuerySpec<int> {
   public int SizeOfSailBoat {get; set;}
}

IQuerySpec<Foo> foo = new Foo {SizeOfSailBoat = 10}.
var result = foo.OrderBy(f => f.SizeOfSailBoat);

在上面的示例中指定了UDT Foo,但我并不关心使用泛型类型参数。

有办法做到这一点吗?

我尝试了以下内容:

public static OrderedSortation<T> OrderBy<T, M, TKey>(
    this T query, 
    Expression<Func<T, TKey>> sort) 
where T : IQuerySpec<M> {
    //business as usual
}

上述工作,但要求明确说明通用参数,以便调用&#34; OrderBy&#34;。我不希望被要求明确说明通用参数。

我也试过这个:

public interface IQuerySpec {}
public interface IQuerySpec<M> : IQuerySpec {}

通过这些界面,我可以这样做:

public static OrderedSortation<T> OrderBy<T, TKey>(
    this T query, 
    Expression<Func<T, TKey>> sort) 
where T : IQuerySpec {
    //business as usual
}

这几乎已经足够了,除了我想要隐藏非泛型IQuerySpec,以便它在我的类库之外不可见。如果我能用这种方式限制非通用接口,我会很高兴:

internal interface IQuerySpec {}

不幸的是,编译器不允许我这样做。

我猜不出我想要的东西是不可能的,但要求以防万一。

2 个答案:

答案 0 :(得分:0)

问题是编译器无法隐式地从M推断出M或T的类型。

所以你需要修复它们,在这里我将T修改为IQuerySpec<M>

并且扩展方法的签名变为

    public static OrderedSortation<IQuerySpec<M>> OrderBy<M, TKey>(
     this IQuerySpec<M> query,
     Expression<Func<IQuerySpec<M>, TKey>> sort)

    {
        //business as usual
    }

作为例子

    class IntQSpec : IQuerySpec<int>
    {
        //your implementation
    }

这是调用示例

var iQS = new IntQSpec();
//do whatever you want with iQS
var ord = iQS.OrderBy(ii=>ii.ToString());
//here I called OrderBy without the need to explicitly specifying the Generic Arguments

这是在visual studio 2013-2015上测试您的代码,突出显示没有错误 请注意我访问了混凝土类型IntQSpec的Z字段 enter image description here

我希望它会有所帮助。

答案 1 :(得分:0)

在我看来,IQuerySpec<M>根本就不需要IQuerySpecTKey应始终M根据我在这里看到的内容。我怀疑替换从而根据您IQuerySpec<M> 打开的愿望,根据IQuerySpec取消M。这是一个证据。请查看它是否符合您要完成的任务,或者是否可以采用最终解决方案的其中一个步骤:

假设事实上需要IQuerySpec<M>M应始终等于TKey,则以下编译并可能与您的实施有关:

假设TKeyM

public interface IQuerySpec<M> { }
public class OrderedSortation<T> {}
public static class IQuerySpecExtensions
{
    public static OrderedSortation<T> OrderBy<T, TKey>(this T query, System.Linq.Expressions.Expression<Func<T, TKey>> sort) where T : IQuerySpec<TKey>
    {
        throw new NotImplementedException(); // business as usual
    }
}
public class Foo : IQuerySpec<int> { public int SizeOfSailBoat {get; set;} }

用法:

public class Demo
{
    public static void Main()
    {
        Foo foo = new Foo {SizeOfSailBoat = 10};
        var result = foo.OrderBy(f => f.SizeOfSailBoat);
    }
}

假设需要多个TKey选项:

如果您需要支持其他类型的其他属性(打开该类以获得更多查询可能性),那么您的类可能如下所示:

public interface IQuerySpec<M> { }
public class OrderedSortation<T> {}
public static class IQuerySpecExtensions
{
    public static OrderedSortation<T> OrderBy<T, TKey>(this T query, System.Linq.Expressions.Expression<Func<T, TKey>> sort) where T : IQuerySpec<TKey>
    {
        throw new NotImplementedException(); // business as usual
    }
}
public class Foo : IQuerySpec<int>, IQuerySpec<string>
{
    public int SizeOfSailBoat {get; set;}
    public string NameOfSailBoat {get; set;}
}

用法:

public class Demo
{
    public static void Main()
    {
        Foo foo = new Foo {SizeOfSailBoat = 10};
        var result = foo.OrderBy(f => f.SizeOfSailBoat);
        result  = foo.OrderBy(f => f.NameOfSailBoat);
    }
}

IQuerySpec<M>缩减为IQuerySpec

在这种情况下,你可以简化为只有这样的IQuerySpec

public interface IQuerySpec { }
public class OrderedSortation<T> {}
public static class IQuerySpecExtensions
{
    public static OrderedSortation<T> OrderBy<T, TKey>(this T query, System.Linq.Expressions.Expression<Func<T, TKey>> sort) where T : IQuerySpec
    {
        throw new NotImplementedException(); // business as usual
    }
}
public class Foo : IQuerySpec
{
    public int SizeOfSailBoat {get; set;}
    public string NameOfSailBoat {get; set;}
}

用法:

public class Demo
{
    public static void Main()
    {
        Foo foo = new Foo {SizeOfSailBoat = 10};
        var result = foo.OrderBy(f => f.SizeOfSailBoat);
        result  = foo.OrderBy(f => f.NameOfSailBoat);
    }
}

以上所有工作都禁止在IQuerySpec上定义没有引用M的实际方法或属性签名。