5

I'm starting to see this pattern appear often in my code:

class Foo { }

interface IBar
{
    Foo Foo { get; }
}

class Bar<TFoo> : IBar where TFoo : Foo
{
    public TFoo Foo { get; private set; }

    Foo IBar.Foo
    {
        get 
        { 
            return Foo; 
        }
    }
}

Some of its benefits are:

  1. An easy way to check whether an object is of the wrapping type (if (something is IBar))
  2. Strong-typed access to TFoo in closed-constructed Bar<>s
  3. Polymorphic access to Foo in interfaced IBars

One could argue that this kind of pattern is everywhere in the framework (e.g. List<T> : IList), but I wonder if this is just a remnant of .NET 1.0, when generics didn't exist.

Off the top of my head, my main concern is that IBar is not necessarily a proper contract that defines what members a "bar" should provide; it's only a hack to access generically typed members.

Also, if I start adding interfaces for that purpose, I quickly end up with hard to maintain parallel inheritance hierarchies.

Should I be worried about spreading this pattern in my code base? If so, what alternative patterns would provide some or all of the 3 benefits listed above?

Because explicitly implementing abstract members is not allowed, this "ideal" solution is not possible:

class Foo { }

class Bar
{
    public abstract Foo Foo { get; }
}

class Bar<TFoo> : Bar where TFoo : Foo
{
    private TFoo foo;

    Foo Bar.Foo
    {
        get
        {
            return foo;
        }
    }

    public new TFoo Foo
    {
        get
        {
            return foo;
        }
    }
}
Lazlo
  • 8,518
  • 14
  • 77
  • 116
  • There is a compile time error in your first example. It seems like there should be `Foo IBar.Foo` instead of `IBar.Foo Foo` – Fabjan Feb 02 '17 at 20:14
  • Sorry, should be fixed now. (The second example isn't valid C#, however). – Lazlo Feb 02 '17 at 20:19
  • In the second example you can go one tiny step further by declaring `Bar` as `abstract`: `abstract class Bar`. – pid Feb 14 '17 at 06:39
  • Honestly, I see the need of interfaces for generics is what weakens generics in C#. In C++ you don't need no interface to do whatever you want. An interface can be applicable when you want to implement one or two. However, when you need lots of methods, interfaces can go really bad. In C++ you don't have to worry about all this. Just an opinion – Everyone Feb 14 '17 at 07:14
  • Did you note that you don't need `new` when there's also an explicitly implemented member with the same name as a regular member? :D – Matías Fidemraizer Feb 14 '17 at 16:53
  • Not certain whether it is the same use case, but [I advocated using non-generic wrapper classes instead of base classes or interfaces in the past](https://whatheco.de/2015/12/15/non-generic-wrapper-instead-of-base-class-or-interface/). In short, by applying the adapter pattern: the wrapper implements both the generic and non-generic interface you want to work with. When using the non-generic interface, you lose type safety and it is up to the caller to only ever pass correct types. – Steven Jeuris Mar 31 '19 at 15:36

4 Answers4

4

For me, the summary is you shouldn't think that you implement interfaces just for the sake of augmenting a generic type parameter with more typing.

AFAIK, you use interfaces to provide which are the contracts to work with a given API. Generics are just a language feature/tool to provide more typing where you would end up doing a lot of casts. Hereby, with generics you limit your API to expect arguments implementing one or more interfaces and also with some requirements using generic constraints.

For example, if you just want to accept implementations of given interface called IWhatever, would you use generics?

public void DoStuff<T>(T whatever)
       where T : IWhatever
{
}

// versus

public void DoStuff(IWhatever whatever)
{
}

BTW, without generics, how you would check that an implementation to IWhatever is a class and has a public constructor? You would end up with reflection and you're code would smell compared to using generics:

public void DoStuff<T>()
       where T : class, IWhatever, new()
{
}

In fact, a generic parameter can constraint that T must inherit a given class and implement one or more interfaces:

public void DoStuff<T>(T arg)
       where T : A, IEquatable<T>, IWhatever, IWhichever, IWherever
{
}

And whether if T inherits a type with or without generic parameters or implements interfaces with or without generic parameters, it's not a good or bad design per se but, again, just a language tool that's suitable to specific cases.

Therefore, your statement...

Off the top of my head, my main concern is that IBar is not necessarily a proper contract that defines what members a "bar" should provide; it's only a hack to access generically typed members.

...describes a particular design flaw instead of an actual problem with typing generics using the wonders of interfaces.

Conclusion: if IBar isn't a proper contract, then you should revisit your architecture and re-think your solution.

More background on the topic

Actually I thought that my original answer implied that I found the whole solution has a design flaw.

In summary, you're using interfaces to expose an association on certain classes which provide the type of the whole association using a generic type parameter. And you argue that you do this to be able to access such association in a less typed context:

However, I sometime need a "less" typesafe context, hence my question.

And then it's when covariance enters in action! See the following code sample:

public class SuperClass 
{
}

public interface IWhatever<out TAssociation>
    where TAssociation : SuperClass
{
    TAssociation Association { get; }   
}

public class SomeImplementation<TAssociation> : IWhatever<TAssociation>
    where TAssociation : SuperClass
{
        public TAssociation Association { get; set; }
}

Now let's define a derived class of SuperClass:

public class DerivedClass : SuperClass
{
}

And see how this works like a charm:

SomeImplementation<DerivedClass> someImpl = new SomeImplementation<DerivedClass>();

// Covariance: you decide the degree of specialization of TAssociation
// interfaces' type parameter. In our case, we'll upcast TAssociation to
// the SuperClass type.
IWhatever<SuperClass> whatever = someImpl;

Clearly this is the way to go since C# 4.0.

I would say that the right way of expressing your requirement is you need a less specialized context instead of a less typed context. Covariance/contravariance is one of the most powerful features available in C# to cover this scenario when generics are involved in the equation.

This practice isn't a code smell per se. In my case, I go for it when I really need to access one or more associations somewhere where I just need to access certain members with a concrete purpose.

For example, if I'm building a tree-style hierarchy, I would define an interface like this:

public interface IHasParent<out TParent>
{
     TParent Parent { get; }
}

Which enables me to do this:

IHasParent<object> withParent = someObject as IHasParent<object>;

if(withParent != null)
{
     // Do stuff here if some given object has a parent object
}

But I don't create interfaces indiscriminately because some day I'll need less typed access to some properties. There should be a well defined purpose. Otherwise, you can end up turning a nice solution into a code smell.

You would say don't repeat yourself but I still feel that there's no definitive answer without analyzing your project code base and checking how you really use this kind of interfaces to solve concrete problems.

So, strictly talking, if you use the whole pattern when it's really required, it should be a good design decision.

Maybe you want to avoid the unavoidable

Based on some chat we've had both the OP and me, I feel that the best conclusion is that the OP wants to avoid the unaviodable.

In an object-oriented language like C# interfaces are the right tool to both define type contracts and expose a subset of a full type implementing some interface.

Also, the OP would love a feature in C# like protocols where a class that implicitly fullfils an interface is enough to consider that it implements the interface which would save up many code lines if C# could have this feature:

public interface IWhatever
{
       void DoStuff();
}

public class X
{
      void DoStuff();
}

public class Y
{
      public void HandleStuff(IWhatever whateverImpls)
      {
      }
}

Y y = new Y();
// Protocols would support passing an instance of X which may not implement
// IWhatever but it implicitly fulfills IWhatever:
y.HandleStuff(new X());

BTW, C# lacks this feature. Therefore, it's a waste of time scratching your head thinking how sweet would be having such feature. You need to deal with what C# has to offer already.

Anyway, if you just need to expose some associations across your object graph and get them selectively, you can use the wonders of interfaces using a more simplified approach than yours. Did you know that you can explicitly implement the same interface more than once if its generic arguments vary?

Why don't you design an interface like this:

public interface IHasAssociation<out TAssociation>
{
    TAssociation Association
    {
        get;
    }
}

public interface IHasManyAssociation<out TEnumerable, out TAssociation>
        where TEnumerable : IEnumerable<TAssociation> 
        where TAssociation : Entity
{
    TEnumerable Association
    {
        get;
    }
}

public class Entity
{
}

public class Company : Entity
{
}

public class CustomerProfile : Entity
{
}

public class Contact : Entity
{
}

public class Customer : 
    IHasAssociation<Company>, 
    IHasAssociation<CustomerProfile>, 
    IHasManyAssociation<IList<Contact>, Contact>
{
    public Company Company
    {
        get;
        set;
    }

    public CustomerProfile Profile
    {
        get;
        set;
    }

    public IList<Contact> Contacts
    {
        get;
        set;
    }

    Company IHasAssociation<Company>.Association => Company;
    CustomerProfile IHasAssociation<CustomerProfile>.Association => Profile;
    IList<Contact> IHasManyAssociation<IList<Contact>, Contact>.Association => Contacts;
}

Definitively this keep things simpler (KISS!) because you don't need a parallel interface object graph definition, you simply define an interface to being able to get an association of a given type:

var customer = new Customer();
customer.Profile = new CustomerProfile();
customer.Company = new Company();
customer.Contacts = new List<Contact>();

var withCompany = customer as IHasAssociation<Company>;
var withCustomerProfile = customer as IHasAssociation<CustomerProfile>;
var withContacts = customer as IHasManyAssociation<IList<Contact>, Contact>;

if (withCompany != null)
{
    Company company = withCompany.Association;
    Console.WriteLine("This object has an associated company!");
}

if (withCustomerProfile != null)
{
    CustomerProfile profile = withCustomerProfile.Association;
    Console.WriteLine("This object has a profile!");
}

if (withContacts != null)
{
    IList<Contact> contacts = withContacts.Association;
    Console.WriteLine("This object has contacts!");
}

Also, see covariance in action:

    if(customer is IHasManyAssociation<IEnumerable<Contact>, Contact>)
    {
        Console.WriteLine("This object has an enumerable of contacts!");
    }

Or here's how you would get all association values of an implementor of one or many IHasAssociation<out TAssociation> interface implementations:

    var entityAssociations = typeof(Customer)
                            .GetInterfaces()
                            .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IHasAssociation<>))
                            .Select(i => i.GetProperty("Association").GetValue(customer));


    foreach(var entityAssociation in entityAssociations)
    {
        Console.WriteLine($"{entityAssociation.GetType().FullName}");
    }

This is the real beauty of generic programming! And remember: you won't need to implement IHasAssociation<out TAssociation>/IHasManyAssociation<out TEnumerable, out TAssociation> indiscriminately. That is, you implement on the classes to which associations need to be extracted in some place where you don't care who's the concrete owner of the association and you just need the association itself.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • "You would end up with reflection" - of course, you still do, as soon as you are looking for any other shape of constructor beside the incredibly limited option of requiring a public, parameterless one. – O. R. Mapper Feb 02 '17 at 20:49
  • @O.R.Mapper Of course... You know that I'm not talking about more complex cases... :D – Matías Fidemraizer Feb 02 '17 at 21:35
  • @O.R.Mapper BTW, if you work out your architecture to take the maximum advantage of `new()` generic constraint, you can go very far without reflection, can't you? – Matías Fidemraizer Feb 02 '17 at 21:36
  • Somehow, `new()` is rarely sufficient for me. I almost invariably end up with requiring a factory of some sort (as a generic interface, or as an additional type parameter, or as a factory function, etc.). – O. R. Mapper Feb 02 '17 at 21:48
  • @O.R.Mapper Clearly it's not a silver bullet but still useful in some scenario. BTW it was just an example. – Matías Fidemraizer Feb 02 '17 at 22:09
  • Coming back to this: I'm not using an interface to provide more typing, I'm using (as you suggest) generics to provide more typing. However, I sometime need a "less" typesafe context, hence my question. The rest of your answer about type constraints is irrelevant to the question. – Lazlo Feb 13 '17 at 23:08
  • @Lazlo So you downvote my answer instead of asking for a clarification or elaborate more some part of the answer itself. – Matías Fidemraizer Feb 13 '17 at 23:20
  • @Lazlo In the other hand, you say that you don't use interface to provide more typing, but again, see the quoted text from your question... – Matías Fidemraizer Feb 13 '17 at 23:21
  • Thanks for the update! Covariance seems to be the correct answer. I have two questions, however: 1) Would `someImpl is IWhatever` return true? 2) Is the interface layer necessary, or could it simply be included in the implementation definition, i.e. `SomeImplementation`? – Lazlo Feb 14 '17 at 13:07
  • @Lazlo Of course, it should return `true` (see this [DotNetFiddle](https://dotnetfiddle.net/1N4sob)). About the second question, sadly variance is only possible with interfaces and delegates... So you need to design interfaces to enjoy the wonders of generic parameters' variance. – Matías Fidemraizer Feb 14 '17 at 13:12
  • @Lazlo Note that in my answer I've used the `as` operator instead of `is`. I would recommend it over `is` because you'll end up casting the reference anyway, and with `as` you check if it's of a given type checking if the reference isn't `null`, and if it evaluates to `true`, you've a reference in the right type already! – Matías Fidemraizer Feb 14 '17 at 13:14
  • In that case, I'm not sure which drawback covariance counters here. It doesn't solve the fact that a matching interface has to be defined for a single type (and potentially a parallel interface hierarchy to types that refer to it). – Lazlo Feb 14 '17 at 13:40
  • @Lazlo There's no drawback in using covariance. It's a tool. About the concern of parallel interface hierarchy, I would say that you can't stick to the idea that it's bad in all cases. At the end of the day, you want to work on abstractions rather than implementations, right? As I've said in my answer,you won't create a parallel object graph defined in both classes and interfaces. You define the interfaces that you really need at some point. I believe that we can go further if we turn this into a chat and you paste somewhere some actual code so I can analyze it and give you a definitive answer – Matías Fidemraizer Feb 14 '17 at 13:46
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/135686/discussion-between-matias-fidemraizer-and-lazlo). – Matías Fidemraizer Feb 14 '17 at 13:46
  • @Lazlo I believe that with the latest update called "Maybe you want to avoid the unavoidable" I provided the best that generics and interfaces can give you in C#! I've completely erased the parallel interface hierarchy while you can still get any association, if you want to! – Matías Fidemraizer Feb 14 '17 at 16:43
  • @Lazlo I've documented this approach here: http://designpattern.ninja/catalog/design-patterns/blind-associations – Matías Fidemraizer Feb 15 '17 at 19:13
  • I know this question is old, but I have a similar problem with my generic "parent/node" classes. I like your IHasParent example, but what happens if your "parent" is an other generic interface. Let's call it IParent which has a AddChild(T child) method. And in this case the T child should be something that implements "IHasParent" for this specific IParent. I can't figure out how to write this, because out T can't be used if you use it as a parameter (like in the AddChild method). – R1PFake May 23 '20 at 11:38
1

In your question, you express the need for a "generic" wrapper type (note I use the term "generic" here independently of any language).

Well, I don't see any problem with that. And if you ask me how to do it with .NET, I would just design this, once for all types, going one step further from you:

interface IWrapper<T>
{
    T BaseObject { get; }
}

Then, a wrapper class would simply be:

class Bar<TFoo> : IWrapper<TFoo> where TFoo : Foo
{
    public TFoo BaseObject { get; private set; }
}

I could go further again and define the non generic one, because it's ofen desirable to have both, as it's sometimes hard to work with generic-only clases/interface with meta/reflection code (but this is really optional):

interface IWrapper
{
    object BaseObject { get; }
}

If you do this, it could be natural to have IWrapper<T> derive from IWrapper, like this:

interface IWrapper<T> : IWrapper
{
    new T BaseObject { get; }
}

And the class would be this:

class Bar<TFoo> : IWrapper<TFoo> where TFoo : Foo
{
    public TFoo BaseObject { get; private set; }

    object IWrapper.BaseObject => BaseObject;
}

PS: as a side note, you can have a look at WCF's ServiceModel ChannelFactory<T> class that has a combined generic/non generic hierarchy somewhat relevant with your question.

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
0

I've found myself in a similar place after developing an API. I have some questions for you and no answers. But once you can answer those questions, maybe you know a bit more about how to address this situation.

I wonder how many classes implement IBar. Are there enough to justify it? Is this an API and you expose it to client code? In how many code points do you leverage the polymorphism of the interface?

Just maybe... those answers can make you question the interface's usefulness.

How many times does this structure emerge? And are you sure it actually does?

I mean, you say you did this:

  • implement lots of code (A);
  • refactor it in one place to clean it up;
  • implement more code (B);
  • refactor it to clean it up;
  • notice that B looks similar to A;
  • implement more code (C);
  • refactor it to clean it up;
  • notice that C looks similar to B (by transitivity also to A);
  • repeat...

Did the structure REALLY emerge, or is it your thinking that mold the code always in the same way? Which comes first?

  • the emergence;
  • the thinking.

This "rinse" and "repeat" approach may be good to start, but just maybe... you've grown out of this methodology and should approach another one:

First design, then implement.

Is this your case? Have you grown that much, that you can finally approach design before implementation?

There's a saying that may apply here:

When you have a hammer, everything looks like a nail.

But let's assume this is not your case. You don't fall into thought cycles and the underlying problem REALLY has this structure, thus your code reflects the problem's structure.

If you really came up with the same thing multiple times, but it's the problem, not your mind playing tricks, then the following may be a good advice.

Stop coding for a day, and think about it away from keyboard.

Which parts are the same, which different? Can't you implement this in an even MORE abstract way (actual pattern) into which you inject the specialized code? Maybe, underlying it all, is a something as simple as a composite pattern, and you could just implement that once and for all, and then reuse it all over the place.

What happened to me was similar, and I ended up with a dependency injection, an abstract factory, an abstract implementation of the composite pattern and an information expert, which took a configuration file and assembled the final object graphs I needed.

It was an excellent, humbling lesson in patterns and architecture, but I regretted actually using it.

Writing the documentation was near impossible and futile. The code became extremely difficult to follow. I always had to look things up and rethink about how to use it correctly. The end result was not that astonishing.

So, if you want to learn and exercise, don't ever stop! But if you want to just get it done and move on, don't overthink it.

Simple is better!

You may be in a place where you try to perfect your code but actually don't need it. You're not writing a new API for M$, are you?

Just take this advice:

In a year or two, you won't be able to understand your own code. You must document it, if you make it that complex. If you can't document it, you'll never reuse it. So you don't need this perfection, it will be throw-away code.

In other words:

The real value is not the code, but the documentation that accompanies it. Without documentation there will be no reuse..

pid
  • 11,472
  • 6
  • 34
  • 63
0

In retrospect, I've learned that the correct term for what I want is return type covariance, which is unfortunately not supported in C#, because the language design team does not consider the benefits of implementing the feature outweigh the cost, even though it preserves type safety. (A proposal has been drafted and completed, but it seems to be abandoned).

With return type covariance, the example code could be written as:

class Foo { }

class Bar
{
    public virtual Foo Foo { get; }
}

class Bar<TFoo> : Bar where TFoo : Foo
{
    public override TFoo Foo { get; }
}

The workaround proposed by Eric Lippert in that linked question is:

class Foo { }

abstract class Bar
{
    protected abstract Foo foo { get; }
    public Foo Foo => foo;
}

class Bar<TFoo> : Bar where TFoo : Foo
{
    protected override Foo foo => this.Foo;
    public new TFoo Foo { get { ... } }
}

It has the downside of duplicating not the inheritance hierarchy, but every covariant-simulated property per level of inheritance!

For further reading on how much clutter simulating covariant return types can bring to your code, consider that implementing ICloneable properly implies adding another virtual method per level of inheritance. I'll leave this as my humble plea for that language feature.

Community
  • 1
  • 1
Lazlo
  • 8,518
  • 14
  • 77
  • 116