как получить доступ к закрытым членам вложенного класса? - PullRequest
15 голосов
/ 12 мая 2010

Справочная информация. Я вложил (родительский) класс E во вложенный класс N с несколькими экземплярами N в E. В вложенном (родительском) классе я делаю некоторые вычисления и задаю значения для каждого экземпляра вложенного класса. Примерно так:

n1.field1 = ...;
n1.field2 = ...;
n1.field3 = ...;
n2.field1 = ...;
...

Это один большой метод eval (в родительском классе). Мое намерение - поскольку все вычисления находятся в родительском классе (они не могут быть выполнены для вложенного экземпляра, потому что это усложнит код) - сделать сеттеры доступными только для родительского класса и геттеров.

А теперь возникла проблема:

  • когда я делаю сеттеры приватными, родительский класс не может получить к ним доступ
  • когда я делаю их общедоступными, каждый может изменить значения
  • и C # не имеют понятия друга
  • Я не могу передать значения в конструкторе, потому что используется ленивый механизм оценки (поэтому экземпляры должны создаваться при обращении к ним - я создаю все объекты, и вычисления запускаются по требованию)

Я застрял - как это сделать (ограничить доступ до родительского класса, не больше, не меньше)?


Я подозреваю, что сначала я получу ответ на вопрос - «но почему вы не разбиваете оценку по каждому полю» - поэтому я отвечаю на этот пример: как вы вычисляете минимальное и максимальное значение Коллекция? Быстро? Ответ - за один проход. Вот почему у меня есть одна функция eval, которая выполняет вычисления и устанавливает все поля одновременно.

Ответы [ 5 ]

48 голосов
/ 12 мая 2010

Вы можете объявить внутри E частный интерфейс IN, явно реализованный N. Этот интерфейс предоставит доступ к членам N, доступным только для E:

public class E
{
    public void Foo()
    {
      IN n = new N();
      n.Field1 = 42;
    }

    public class N : IN
    {
        private int _field1;

        int IN.Field1
        {
            get { return _field1; }
            set { _field1 = value; }
        }
    }

    private interface IN
    {
        int Field1 { get; set; }
    }
}
7 голосов
/ 25 февраля 2013

Другая альтернатива - оставить члены, которые вы хотите, чтобы они были закрытыми. , если ваш (вложенный) класс является закрытым. Если поля частного класса являются открытыми, они будут доступны только для включающего класса.

public class E
{
    public void Foo()
    {
      IN n = new N();
      n.field1 = 42;
    }

    class N : IN
    {
        public int _field1;
    }
}

Теперь N виден только для E, поэтому n._field1 общедоступность имеет значение только для E, и вы в безопасности ..

7 голосов
/ 12 мая 2010

Если вы можете поместить родительский и дочерний классы в другую сборку, вы можете использовать internal для сеттеров. Обычно это происходит в дикой природе.

EDIT

Ответ Томаса Левеска дал мне идею:

class Program
{
    static void Main(string[] args)
    {
        E myE = new E();

        Console.WriteLine("E.N1.Field1 = " + myE.N1.Field1);
        Console.WriteLine("E.N2.Field1 = " + myE.N2.Field1);
    }

    public interface IN
    {
        int Field1 { get; }
    }

    public class E
    {
        private N _n1 = new N();
        private N _n2 = new N();

        public E()
        {
            _n1.Field1 = 42;
            _n2.Field1 = 23;
        }

        public IN N1
        {
            get { return _n1; }
        }

        public IN N2
        {
            get { return _n2; }
        }

        private class N : IN
        {
            private int _field1;

            public int Field1
            {
                get { return _field1; }
                set { _field1 = value; }
            }
        }
    }
}

В зависимости от того, как вам нужно выставить дочерний класс N, это может сработать.

1 голос
/ 26 февраля 2014

Это старый вопрос, но здесь приводится возможное решение, которое не использует интерфейсы.

Вы можете иметь статическую функцию во внутреннем классе, которая устанавливает делегаты во внешнем классе, например:

public class Outer {
    private delegate void _operateDlg(Inner inner, bool value);
    private static _operateDlg _validate;

    static Outer() {
        Inner.Init();
    }

    public void Set(Inner inner, bool value) {
        _validate(inner, value);
    }

    public class Inner {
        public bool IsValid {get; private set; }
        public static void Init() {
            Outer._validate += delegate(Inner i, bool value) {
                i.IsValid = value;
            };
        }
    }
}

Вы можете поместить все виды различных делегатов во внешний класс, который вы назначаете с помощью метода Inner.Init (), например, методы, которые возвращают экземпляр класса Inner через закрытый конструктор или методы получения / установки определенного поля. ,

Если вы не против иметь в своем внутреннем классе дополнительную статическую функцию Init (), то это не должно измениться. Но если вы не хотите, чтобы метод Init () был видимым, вы можете использовать рефлексию для его вызова:

using System.Reflection;

public class Outer {
    private delegate void _operateDlg(Inner inner, bool value);
    private static _operateDlg _validate;

    static Outer() {
        typeof(Inner).GetMethod("Init",
            BindingFlags.Static | BindingFlags.NonPublic).Invoke(null, null);
    }

    public void Set(Inner inner, bool value) {
        _validate(inner, value);
    }

    public class Inner {
        public bool IsValid {get; private set; }
        private static void Init() {
            Outer._validate = delegate(Inner i, bool value) {
                i.IsValid = value;
            };
        }
    }
}

Я знаю, что в любом случае можно использовать Reflection, чтобы обойти ограничения частного доступа, но, на мой взгляд, использовать его только для вызова одного метода Init (), который затем назначает соответствующих делегатов, - намного более чистое и более универсальное решение. Альтернативой может быть вызов рефлексии для каждого отдельного делегата, который вы, возможно, захотите создать, и даже тогда могут быть ограничения (например, невозможность создания делегатов для конструкторов).

Приведенное выше решение не только поддерживает конструкторы обтекания, но и будет использовать Reflection только один раз за время существования программы, поэтому не должно быть заметного снижения производительности, за исключением того факта, что вы используете делегатов для достижения того, что должно были разрешены в качестве прямого доступа в первую очередь. Я не знаю, почему C # не поддерживает это, и я не могу придумать вескую причину, почему это не так.

0 голосов
/ 12 мая 2010

сделать поля "внутренними защищенными"

если вложенные классы являются частными, вы можете использовать obly "internal" для этих полей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...