views:

1724

answers:

5

Given the following inheritance tree, what would be the best way of implementing it in a way that works?

abstract class Foo<T> : IEnumerable<T>
{
    public abstract Bar CreateBar();
}

class Bar<T> : Foo<T>
{
    // Bar's provide a proxy interface to Foo's and limit access nicely.
    // The general public shouldn't be making these though, they have access
    // via CreateBar()
    protected Bar(Foo base)
    {
        // snip...
    }
}

class Baz<T> : Foo<T>
{
    public Bar CreateBar()
    {
        return new Bar(this);
    }
}

This fails with: 'Bar.Bar()' is inaccessible due to its protection level.

I don't want the constructor being public, only classes that inherit from Foo should be able to create Bars. Bar is a specialised Foo, and any type of Foo should be able to create one. Public internal is an 'option' here, as the majority of the predefined extensions to Foo will be internal to the DLL, but I consider this a sloppy answer, since anyone who comes along later who wants to create their own type of Foo or Baz (which is likely to happen) will be stuck with a default CreateBar() implementation, which may or may not meet their needs.

Perhaps there is a way of refactoring this to make it work nicely? I'm banging my head on the wall trying to design this so it'll work though.

Edit (More info):

Slightly more concrete: Foo is implementing IEnumerable and long story short, Bar is providing the same interface, but to a limited subset of that enumerable object. All Foo's should be able to create subsets of themselves (ie. Bar) and return it. But I don't want to have everyone who ever wants to implement a Foo to have to worry about this, because Bar will do the proxying and worry about limiting the range, etc.

A: 

Would it be possible for you to make Baz a nested type within Bar? That's the only way you'll give it more access to Bar than it would otherwise have. Just having the same parent class only gives it access to protected members of Foo, and Foo doesn't have special access to Bar. I suspect there are other tortuous ways of doing this with nested types, but really it's going to be quite unpleasant for maintenance engineers.

It's quite an odd design though, to force one derived class to create an instance of a different class derived from the same base class. Is that really what you need? Perhaps if you put this in more concrete terms it would be easier to come up with alternative designs.

Jon Skeet
Slightly more concrete: Foo is implementing IEnumerable<T> and long story short, Bar is providing the same interface, but to a limited subset of that enumerable object. All Foo's should be able to create subsets of themselves and return it.
Matthew Scharley
I would go with what Jon said, encapsulate, else make the constructor protected internal.
leppie
s/encapsulate/nested/
leppie
A: 

C# doesn't provide a direct equivalent of the C++ friend keyword. Seems like your design is requiring this sort of construct.

In C++ you could designate that a specific class has access to the private/protected members of another class by using "friend". Note: this is not the same as C# internal, modifier which gives access to all classes within the same assembly.

Looking at your design, it seems you are trying to do something along the lines that would require the C++ style friend. Jon Skeet is right, the usual design in C# to make up for this is to use a nested class.

This forum post explains further and shows some examples of how to do this.

Ash
Nesting Bar in Foo doesn't help me access the protected constructor in Bar from Foo, and nesting Foo in Bar is impossible while Bar inherits from Foo... :( maybe with interfaces, but...
Matthew Scharley
What I'm saying is perhaps you need to change your design to a more straightforward approach. I'm sure there's good reasons the C# team did not include the C++ style friend construct in the language.
Ash
They probably didn't include it because the assembly is the unit of deployment so the "internal" keyword is friendly enough. If a class isn't to be trusted with seeing an internal member of another class within the same assembly it shouldn't even be in the same assembly.
Mark Cidade
+1  A: 

You can access Bar's constructor through a nested type within Foo:

abstract class Foo<T> : IEnumerable<T>
{
  public abstract Bar<T> CreateBar();

  protected Bar<T> CreateBar(Foo<T> f) { return new FooBar(f); }

  private class FooBar : Bar<T> 
   { public FooBar(Foo<T> f) : base(f) {}   
   }
}

class Bar<T> : Foo<T>
{ protected Bar(Foo<T> @base) {}
}

class Baz<T> : Foo<T>
{
    public override Bar<T> CreateBar() 
    {
        return CreateBar(this);
    }
}
Mark Cidade
Wow, interesting solution, and it works too... Seems counterintuitive and messy, but hey... what happens with private classes and .GetType()?
Matthew Scharley
don't do that : ) Actually, the type is accessible through reflection, so you'll still see FooBar that way. The contract's intent, though is that it returns a *Bar* and that *is* fulfilled. A cleaner solution would invlove an IBar interface.
Mark Cidade
God help the poor support developer who has to maintain this code such as this. I think I've said it before but @monoxide you really should use KISS principles in your design.
Ash
I'm more thinking more about naive client code that might do "if (someBarVar is Bar<int>)"
Matthew Scharley
Foo<T>.FooBar should only act as a proxy class to Bar<T> -- it's not supposed to have any extra stuff in it so it shouldn't be any more a maintainability problem than any other solution to the problem (i.e., no "friend" keyword in C#)
Mark Cidade
@monoxide It should still work for is/as operators since Foo<int>.FooBar still inherits from Bar<int>. If they do something like "if (bar.GetType().Name=="Bar") then *that* would be an issue--with their type-checking techniques, that is.
Mark Cidade
+1  A: 

Okay, new answer:

  1. Split Bar into an interface and a concrete class.
  2. Express the public abstract method in terms of IBar.
  3. Make Bar a private nested class in Foo, implementing IBar. Give it an internal constructor which you can call from Foo.
  4. Write a protected method in Foo which creates an instance of Bar from itself. Classes deriving from Foo can use this to implement the abstract method if just proxying is good enough, and classes with more complicated needs can just implement IBar directly. You could even change the abstract method to a virtual one, and create a new Bar from "this" by default.

EDIT: One variant on this would be to make Bar a protected nested class within Foo, with a public constructor. That way any derived class would be able to instantiate it for themselves, but no unrelated class would be able to "see" it at all. You'd still need to separate the interface from the implementation (so that the interface can be public) but I think that's a good thing anyway.

Jon Skeet
Or change the return type to Foo, which is possible and valid too I suppose. The interface definition is currently in the abstract baseclass Foo anyway, with some default IEnumerable implementation (hence the class and not interface). Class makes more sense logically in this particular case too.
Matthew Scharley
+1  A: 

Forget, for a moment, that Bar derives from Foo. If you do this, it sounds like the problem is "How do I make it so that every subclass of Foo can create a Bar, even if the subclass doesn't have access to Bar's constructor?"

That's a pretty easy problem to solve:

public class Foo
{
   protected static Bar CreateBarInstance()
   {
      return new Bar();
   }
   public virtual Bar CreateBar()
   {
      return CreateBarInstance();
   }
}

public class Bar
{
    internal Bar()
    {
    }
}

public class Baz : Foo
{
    public override Bar CreateBar()
    {
        Bar b = base.CreateBar();
        // manipulate the Bar in some fashion
        return b;
    }
}

If you want to guarantee that the subclasses of Foo don't have access to Bar's constructor, put them in a different assembly.

Now, to derive Bar from Foo, it's a simple change:

public class Bar : Foo
{
    internal Bar()
    {
    }
    public override Bar CreateBar()
    {
        throw new InvalidOperationException("I'm sorry, Dave, I can't do that.");
    }

}

"I don't want the constructor being public." Check.

"Only classes that inherit from Foo should be able to create Bars." Check.

"Any type of Foo should be able to create one." Check,

"Anyone who comes along later who wants to create their own type of Foo or Baz (which is likely to happen) will be stuck with a default CreateBar() implementation, which may or may not meet their needs." This is pretty highly dependent on what has to happen in the Foo.CreateBar() method, I think.

Robert Rossney