【问题标题】:Open Generic Constraint打开通用约束
【发布时间】:2016-01-09 11:45:36
【问题描述】:

给定如下界面:

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&lt;M&gt; 的一些变体。然后我可以如下调用扩展方法:

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

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

上面的例子中指定了UDTFoo,但我不在乎使用什么泛型类型参数。

有没有办法做到这一点?

我尝试了以下方法:

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

上述方法有效,但需要明确声明通用参数才能调用“OrderBy”。我不想被要求明确声明泛型参数。

我也试过这个:

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

很遗憾,编译器不允许我这样做。

我猜我想要什么是不可能的,但以防万一。

【问题讨论】:

  • 你能举个例子说明你希望使用的语法是什么样的吗?
  • @Mike:我添加了一个调用示例。

标签: c# generics


【解决方案1】:

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

所以你需要修复它们,这里我将 T 修复为 IQuerySpec&lt;M&gt;

扩展方法的签名变成了

    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 上的代码测试,突出显示的地方没有错误 请注意,我访问了具体类型的 Z 字段IntQSpec

我希望它会有所帮助。

【讨论】:

  • 我已经修改了我的问题,以澄清表达式树必须能够访问具体的 IQuerySpec 的成员。在我的示例中,我访问“SizeOfSailBoat”,它在具体的 Foo 类中可用,但在 IQuerySpec 接口上不可用。所以你在这里的例子是行不通的。
  • 好的,如果您使用 Visual Studio 2010 及更早版本,则编译器无法隐式执行您想要的操作,但在 Visual Studio 2013-2015 上可以在不指定通用参数的情况下工作
  • 我正在使用 VS 2015。在您提供的示例中,您将 M 传递到表达式树中。表达式树必须能够访问 T 而不是 M
  • 是的,您的最新示例确实有效 - 我在我的问题中也使用了相同的示例。在我的问题中,我说使用非通用 IQuerySpec 的解决方案“几乎足够好”。问题是,如果我必须使用非通用的IQuerySpec 接口,那么我想要一种将其隐藏为internal 的方法——但这似乎是不可能的。
  • 如果您提供您真正想要的示例,我们可能会找到解决您所面临问题的方法
【解决方案2】:

在我看来,IQuerySpec&lt;M&gt; 要么根本不需要,而应该只是 IQuerySpecTKey 应该总是 M 根据我在这里看到的。我怀疑 替换 并因此将 IQuerySpec&lt;M&gt; 替换为 IQuerySpec 是您真正想要的,因为您希望 M 打开。这是一个证明。请查看它是否符合您要完成的目标,或者通往最终解决方案的其中一个步骤是否更接近:

假设实际上需要IQuerySpec&lt;M&gt; 并且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&lt;M&gt; 减少到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 的实际方法或属性签名,否则上述所有工作都有效。

【讨论】:

    猜你喜欢
    • 2020-07-29
    • 2021-11-08
    • 2018-03-12
    • 2018-09-17
    • 2018-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-26
    相关资源
    最近更新 更多