我可以忽略C#接口中的泛型类型吗?

时间:2014-06-03 20:12:03

标签: c# generics interface sieve.net

背景

我正在开始研究名为Sieve.NET的小型OSS库。

签名允许某人定义Sieve,如下所示:

new EqualitySieve<ABusinessObject>().ForProperty(x => x.AnInt);

这实际上会返回Sieve<ABusinessObject, int>,但我已尽力确保用户不必过多关心该部分。

任务

我想找到一种方法来设置一个接口,我根本不关心属性类型 - 只是它始终是一致的。

基本上,我希望能够声明一个ISieve<TFilterObjectType>,并且能够让该接口定义如下:

ISieve<TFilterObjectType, TTypeIDontCareAbout> ForValue(TTypeIDontCareAbout);

我的目标是能够创建一个由ISieve<ABusinessObject>而非ISieve<ABusinessObject, int>组成的课程。

问题

  • 接口是否有办法声明一种实际上是通配符的类型,并说“我不关心这是什么类型,只是它是一致的?”

我最初的研究表明没有,但我希望被证明是错误的。

更新&amp;澄清

我真正想弄清楚的是:

  • 我允许用户创建EqualitySieve<ABusinessObject>().ForProperty(x=>x.AnInt)
  • 这实际上会向用户返回EqualitySieve<ABusinessObject, int>,但由于它是一个流畅的界面,我将其删除而不必关心该部分。
  • 我希望EqualitySieveLessThanSieve等实施ISieve<ABusinessObject>
  • 我希望ISieve<ABusinessObject执行合同,我可以允许某人致电ForValues()并希望它返回一个包含更新值的ISieve。
  • 但是,此时EqualitySieve<ABusinessObject>实际上是EqualitySieve<ABusinessObject, int>。但我当时并不特别关心房产类型。
  • 基本上,由于我正在将EqualitySieve<ABusinessObject, int>部分抽象出去,我还想知道在通过界面引用对象时是否可以抽象出来。
  • 长期计划是我想要一个SieveLocator,其中类可以实现IFindableSieve<ABusinessObject>,理想情况下会返回ISieve<ABusinessObject>。然后我的目标是能够找到给定对象的Sieves。
  • 所以我认为这可能是我设计的限制,我必须找到其他方法。对此提出的任何建议或对我可能没有看到的模式的引用也会有所帮助。

2 个答案:

答案 0 :(得分:3)

可能是一些技巧,所以调用者不需要在泛型方法上指定类型(想想LINQ如何工作),但不幸的是你的研究是正确的,没有办法在编写使用该类型的类时推断出类型。

最接近它的是具有两层接口,其中外层不使用任何依赖TTypeIDontCareAbout类型的函数。

interface ISieve<TFilterObjectType,TTypeIDontCareAbout> : ISieve<TFilterObjectType> 
{
    TFilterObjectType ForValue(TTypeIDontCareAbout forValue);
}

interface ISieve<TFilterObjectType> 
{
    TFilterObjectType SomeOtherFunction();
}

我不知道如何解决你所有的问题,但我认为蒂莫西的方法是你想要的两点

  
      
  • 我允许用户创建EqualitySieve<ABusinessObject>().ForProperty(x=>x.AnInt)
  •   
  • 这实际上会向用户返回EqualitySieve<ABusinessObject, int>,但由于它是一个流畅的界面,我将其删除而不必关心该部分。
  •   
interface ISieve<TFilterObjectType> 
{
    TFilterObjectType SomeOtherFunction();
    EqualitySieve<TFilterObjectType, T> ForProperty<T>(Func<TFilterObjectType, T> selector);
    EqualitySieve<TFilterObjectType, T> ForProperty<T>(Expression<Func<TFilterObjectType, T>> selector); //This is how you would do it if you wanted IQueryable support.
}

答案 1 :(得分:3)

您可以在界面和界面的方法上放置泛型类型参数。因此,下面的示例将定义一个通用接口,其中F方法采用其中之一&#34;我不关心这是什么类型,只关注它是否一致&#34;参数。

interface I<T>
{
    //The generic type parameter U is independent of T.
    //Notice how F "forwards" the type U from input to output.
    Tuple<T, U> F<U>(U u);
}

考虑以下玩具类:

class C : I<char>
{
    public char Value { get; set; }
    public Tuple<char, U> F<U>(U u)
    {
        return Tuple.Create(Value, u);
    }
}

以下是一些示例用法:

I<char> instance = new C { Value = '!' };
Tuple<char, int> x = instance.F(5); // ('!', 5)
Tuple<char, string> y = instance.F("apple"); // ('!', "apple")

更新

  
      
  • 我允许用户创建EqualitySieve<ABusinessObject>().ForProperty(x=>x.AnInt)
  •   
  • 这实际上会向用户返回EqualitySieve<ABusinessObject, int>,但由于它是一个流畅的界面,我将其删除而不必关心该部分。
  •   
  • 我希望EqualitySieveLessThanSieve等实施ISieve<ABusinessObject>
  •   

使用我上面提到的想法,你可以做你想要的(我认为)。

interface ISieve<T>
{
    //It's still not clear what you actually want in this interface...
}

static class Sieve
{
    public EqualitySieve<T> Equality<T>()
    {
        return new EqualitySieve<T>();
    }

    public LessThanSieve<T> LessThan<T>()
    {
        ...
    }
}    

class EqualitySieve<T> : ISieve<T>
{
    //Notice how the property type P is independent of T
    //and can be inferred here from the passed expression
    public EqualitySieve<T, P> ForProperty<P>(
        Expression<Func<T, P>> propertyExpression)
    {
        return new EqualitySieve<T, P>
        {
            PropertyExpression = propertyExpression
        };
    }
}

class EqualitySieve<T, P> : ISieve<T>
{
    public Expression<Func<T, P>> PropertyExpression { get; set; }
}

用法:

//Assuming MyObject.MyProperty is an int property
//s has type EqualitySieve<MyObject, int>
var s = Sieve.Equality<MyObject>().ForProperty(x => x.MyProperty);