views:

182

answers:

3

I'm trying to access a method that is marked as internal in the parent class (in its own assembly) in an object that inherits from the same parent.

Let me explain what I'm trying to do...

I want to create Service classes that return IEnumberable with an underlying List to non-Service classes (e.g. the UI) and optionally return an IEnumerable with an underlying IQueryable to other services.

I wrote some sample code to demonstrate what I'm trying to accomplish, shown below. The example is not real life, so please remember that when commenting.

All services would inherit from something like this (only relevant code shown):

public class ServiceBase<T>
{
    protected readonly ObjectContext _context;
    protected string _setName = String.Empty;

    public ServiceBase(ObjectContext context)
    {
        _context = context;
    }

    public IEnumerable<T> GetAll()
    {
        return GetAll(false);
    }

    //These are not the correct access modifiers.. I want something
    //that is accessible to children classes AND between descendant classes
    internal protected IEnumerable<T> GetAll(bool returnQueryable)
    {
        var query = _context.CreateQuery<T>(GetSetName());
        if(returnQueryable)
        {
            return query;
        }
        else
        {
            return query.ToList();
        }
    }

    private string GetSetName()
    {
        //Some code...
        return _setName;
    }



}

Inherited services would look like this:

public class EmployeeService : ServiceBase<Employees>
{
    public EmployeeService(ObjectContext context)
        : base(context)
    {

    }

}

public class DepartmentService : ServiceBase<Departments>
{
    private readonly EmployeeService _employeeService;

    public DepartmentService(ObjectContext context, EmployeeService employeeService) : base(context)
    {
        _employeeService = employeeService;
    }

    public IList<Departments> DoSomethingWithEmployees(string lastName)
    {
        //won't work because method with this signature is not visible to this class
        var emps = _employeeService.GetAll(true);

        //more code...
    }
}

Because the parent class lives is reusable, it would live in a different assembly than the child services. With GetAll(bool returnQueryable) being marked internal, the children would not be able to see each other's GetAll(bool) method, just the public GetAll() method.

I know that I can add a new internal GetAll method to each service (or perhaps an intermediary parent class within the same assembly) so that each child service within the assembly can see each other's method; but it seems unnecessary since the functionality is already available in the parent class.

For example:

    internal IEnumerable<Employees> GetAll(bool returnIQueryable)
    {
        return base.GetAll(returnIQueryable);
    }

Essentially what I want is for services to be able to access other service methods as IQueryable so that they can further refine the uncommitted results, while everyone else gets plain old lists.

Any ideas?

EDIT

You know what, I had some fun playing a little code golf with this... but ultimately I wouldn't be able to use this scheme anyway because I pass interfaces around, not classes.

So in my example GetAll(bool returnIQueryable) would not be in the interface, meaning I'd have to do casting, which goes against what I'm trying to accomplish.

I'm not sure if I had a brain fart or if I was just too excited trying to get something that I thought was neat to work. Either way, thanks for the responses.

+3  A: 

What's wrong with the obvious answer of making the method public?

Accessibility is not secure anyway, so "evil people" could use reflection to bypass whatever modifier you put on. And since you want to call these methods from unrelated "sibling" classes they should be public, as there is no "sibling" accessibility modifier.

Alternative suggestion: Apply the [assembly:InternalsVisibleTo("SomeOtherAssembly")] attribute to grant other business domain assemblies access to the internal members.

Note that this adds a maintenance burden (if you rename or add assemblies), uses "magic strings", and negates the original meaning of internal (since all internal members now will be visible to these other assemblies as well).

Morten Mertner
I'm not trying to hide from "evil people," our policy is that the business layer should return lists to the above layer. Having services return iqueryables to each other just makes things a little more convenient within the business layer.
Giovanni Galbo
You could use "protected internal" and the InternalsVisibleTo attribute to grant specific assemblies access to the internal members, if you really care that much about it. I'd recommend against it as it needs to be maintained (e.g. if you add another assembly or decide to rename one), but might be a solution for you.
Morten Mertner
I considered InternalsVisible, but I don't think it would work for me. I probably didn't make it clear in the original question; the base class would be used by many different projects by different teams in the company; so it would be very weird to have dozens of InternalsVisible attributes in the base class. I sort of need the opposite of this, where outsiders "opt in" to get access to the method. I'm pretty sure this doesn't exist though. Thanks for the suggestion though!
Giovanni Galbo
A: 

When your descendant classes would live in the same assembly, this would be easier. However, the following 'trick' also works when your classes live in separate assemblies (changing the interface's internal keyword to public), but it would only prevent the method from being called when the object is not cast. For example, casting an InheritedClass object to an IBaseInterface would allow anyone to call the GetValue method. This is something you can't really prevent, because even if there where some combination of keywords which could do what you want, someone could still call the methods through reflection.

internal interface IBaseInterface {
    string GetValue();
}

public abstract class MyBase : IBaseInterface {
    string IBaseInterface.GetValue()
    { return "MyBase"; }
}

public class InheritedClass : MyBase {
    public void PrintValue(IBaseInterface someclass)
    { Console.WriteLine(someclass.GetValue()); }
}
Virtlink
Yea, this alternative kind of handles it from the other hand as the alternative I provided in the question (have a base class that inherits from the original class so that all children classes from within the same assembly can see each other's internal methods).I kind of wanted to avoid the extra work, but I don't think it's possible.
Giovanni Galbo
A: 

Perhaps I am missing something, but you should be able to mark it as protected internal and accomplish your goals.

namespace Foo
{
    class A
    {
        protected internal void D() { Console.WriteLine(this.ToString() + " says 'Blah'"); }
    }
}

namespace Bar
{
    class B : Foo.A 
    {
        public B()
        {
        }
    }
}

namespace Baz
{
    class C : Foo.A
    {
        public C()
        {
            D();
            Bar.B b = new Bar.B();
            b.D();

            Foo.A a = new Foo.A();
            a.D();
        }
    }
}

To see the output

Baz.C c = new Baz.C();

This is all legal code. Foo.A is the base, Bar.B and Baz.C inherit from Foo.A. void D is a protected internal member of A. Baz.C can invoke D() as expected, as well as create instances of Bar.B and Foo.A and invoke their D() methods as well.

protected internal members are visible to all descendant classes in any assembly and all classes within the same assembly as the base. The accessibility ends outside of those bounds. Non-descendant classes within the same assemblies as the the children would not be able to see the member.

Anthony Pegram
I want objects that share an ancestor to be able to access each other's method. These classes would be defined in a different ancestor as the base class.
Giovanni Galbo
Are these methods *other* than those that are inherited? Like I said, siblings will be able to see each other's inherited methods as long as they are at least classified as protected. Non-inherited methods, on the other hand, will need to be public in order to be visible across assemblies.
Anthony Pegram