Can I ignore a generic type in a C# interface?

c#, generics, interface, sieve.net

Solution

You can place generic type parameters on both the interface and the interface's methods. So the following example would define a generic interface where the `F` method takes one of these "I don't care what type this is, only that it's consistent" parameters.

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);
}

Consider the following toy class:

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

Here's some example usage:

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

Updates

- I allow users to create an `EqualitySieve<ABusinessObject>().ForProperty(x=>x.AnInt)`.

- This actually returns an `EqualitySieve<ABusinessObject, int>` to the user, but since it's a fluent interface I remove them from having to care about that part.

- I would like `EqualitySieve`, `LessThanSieve`, etc. to implement `ISieve<ABusinessObject>`.

Using the ideas I mentioned above, you can do what (I think) you want.

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

Usage:

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

Problem

Background I'm starting work on a little OSS library called Sieve.NET. The signature lets someone define a `Sieve` as follows: ``` new EqualitySieve<ABusinessObject>().ForProperty(x => x.AnInt); ``` This actually returns a `Sieve<ABusinessObject, int>`, but I've done my best to ensure that users don't have to care about that part too too much. The Task I would like to find a way to put an interface on this, where I don't care about the property type at all -- only that it is consistent throughout. So essentially, I would like to be able to declare an `ISieve<TFilterObjectType>`, and by able to have that Interface define something like: ``` ISieve<TFilterObjectType, TTypeIDontCareAbout> ForValue(TTypeIDontCareAbout); ``` My goal is to be able to have a class composed from `ISieve<ABusinessObject>` and not `ISieve<ABusinessObject, int>`. Question - Is there a way for an interface to declare a type that effectively is a wildcard, and says "I don't care what type this is, only that it's consistent?" My initial research says no but I'm hoping to be proven wrong. Updates & Clarifications What I'm really trying to figure out is: - I allow users to create an `EqualitySieve<ABusinessObject>().ForProperty(x=>x.AnInt)`. - This actually returns an `EqualitySieve<ABusinessObject, int>` to the user, but since it's a fluent interface I remove them from having to care about that part. - I would like `EqualitySieve`, `LessThanSieve`, etc. to implement `ISieve<ABusinessObject>`. - I would like `ISieve<ABusinessObject` to enforce a contract whereby I could allow someone to call `ForValues()` and expect it to return an ISieve with the updated values. - However, at that point, the `EqualitySieve<ABusinessObject>` is actually an `EqualitySieve<ABusinessObject, int>`. But I don't particularly care about the property type at that point. - Essentially, since I'm abstracting the away the `EqualitySieve<ABusinessObject, int>` portion, I also wanted to see if I could abstract that away when referring to objects via the interface. - The long-term plan is that I want to have a SieveLocator, where classes can implement an `IFindableSieve<ABusinessObject>` that ideally would return an `ISieve<ABusinessObject>`. Then my goal would be to be able to find those Sieves for a given object. - So I'm thinking this is likely a limitation of my design and I'll have to find some other way around it. Any suggestions on that or references to a pattern I might not be seeing would be helpful as well.

Original source