Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.
like image 324
SeanKilleen Avatar asked Jun 03 '14 20:06

SeanKilleen


People also ask

How do you restrict a generic type?

Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.

Can we override generic method in C#?

Similar to John Carpenter's answer, you can override the generic method with the same generic method, but simply use the as operator to check and cast it to the desired type. This has the added benefit of using null-testing to check if the conversion worked.

Can generic class have non generic method?

Yes, you can define a generic method in a non-generic class in Java.

Is it possible to inherit from a generic type?

An attribute cannot inherit from a generic class, nor can a generic class inherit from an attribute.


1 Answers

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);
like image 154
Timothy Shields Avatar answered Sep 28 '22 10:09

Timothy Shields