Как получить доступ к внутреннему методу объекта-потомка в C # - PullRequest
2 голосов
/ 09 апреля 2010

Я пытаюсь получить доступ к методу, который помечен как внутренний в родительском классе (в его собственной сборке) в объекте, который наследуется от того же родителя.

Позвольте мне объяснить, что я пытаюсь сделать ...

Я хочу создать классы Service, которые возвращают IEnumberable с базовым списком в не-Service классы (например, пользовательский интерфейс) и, необязательно, возвращают IEnumerable с базовым IQueryable другим сервисам.

Я написал некоторый пример кода, чтобы продемонстрировать, что я пытаюсь выполнить, как показано ниже. Пример не является реальной жизнью, поэтому, пожалуйста, помните это при комментировании.

Все сервисы наследуются от чего-то подобного (показан только соответствующий код):

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



}

Унаследованные сервисы будут выглядеть так:

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

Поскольку жизни родительского класса можно использовать повторно, он будет жить в другой сборке, чем дочерние службы. Поскольку GetAll (bool returnQueryable) помечен как внутренний, дочерние элементы не смогут видеть метод GetAll (bool) друг друга, а только открытый метод GetAll ().

Я знаю, что могу добавить новый внутренний метод GetAll для каждой службы (или, возможно, промежуточный родительский класс в той же сборке), чтобы каждая дочерняя служба в сборке могла видеть метод друг друга; но это кажется ненужным, поскольку функциональность уже доступна в родительском классе.

Например:

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

По сути, я хочу, чтобы службы имели возможность доступа к другим методам служб как IQueryable, чтобы они могли дополнительно уточнить незафиксированные результаты, в то время как все остальные получают простые старые списки.

Есть идеи?

EDIT

Знаете, я немного повеселился, играя в небольшой гольф-код с этим ... но в конечном итоге я не смог бы использовать эту схему в любом случае, потому что я пропускаю интерфейсы, а не классы.

Так что в моем примере GetAll (bool returnIQueryable) не будет в интерфейсе, а это значит, что мне придется выполнять приведение типов, что противоречит тому, что я пытаюсь выполнить.

Я не уверен, был ли у меня пердеть мозг или я был слишком взволнован, пытаясь получить что-то, что, как мне казалось, было полезно для работы. В любом случае, спасибо за ответы.

Ответы [ 3 ]

3 голосов
/ 09 апреля 2010

Что не так с очевидным ответом обнародования метода?

Доступность в любом случае небезопасна, поэтому «злые люди» могут использовать отражение, чтобы обойти любой модификатор, который вы надеваете. А поскольку вы хотите вызывать эти методы из не связанных друг с другом классов-братьев, они должны быть общедоступными, так как модификатор доступности «одного брата» отсутствует.

Альтернативное предложение: примените атрибут [assembly:InternalsVisibleTo("SomeOtherAssembly")], чтобы предоставить другим сборкам бизнес-доменов доступ к внутренним элементам.

Обратите внимание, что это увеличивает нагрузку на обслуживание (если вы переименовываете или добавляете сборки), использует "магические строки" и отрицает первоначальное значение внутренних (поскольку все внутренние элементы теперь будут видны и этим другим сборкам).

0 голосов
/ 09 апреля 2010

Возможно, я что-то упускаю, но вы должны пометить это как protected internal и достичь своих целей.

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

Чтобы увидеть вывод

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

Это все юридический код. Foo.A является базой, Bar.B и Baz.C наследуются от Foo.A. void D является защищенным внутренним членом A. Baz.C может вызывать D (), как и ожидалось, а также создавать экземпляры Bar.B и Foo.A и также вызывать их методы D ().

protected internal члены видны всем классам-потомкам в любой сборке и всем классам в той же сборке, что и база. Доступность заканчивается за пределами этих границ. Классы, не являющиеся потомками в тех же сборках, что и дочерние элементы, не смогут видеть элемент.

0 голосов
/ 09 апреля 2010

Когда ваши потомки будут жить в одной сборке, это будет проще. Тем не менее, следующий «трюк» также работает, когда ваши классы живут в отдельных сборках (изменяя ключевое слово internal интерфейса на public), но это предотвратит вызов метода только тогда, когда объект не приведен. Например, приведение объекта InheritedClass к IBaseInterface позволит любому вызвать метод GetValue. Это то, что вы не можете предотвратить, потому что даже если есть какая-то комбинация ключевых слов, которая может делать то, что вы хотите, кто-то все равно может вызывать методы через рефлексию.

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