Подтвердить что ты не робот

Как получить доступ к закрытым членам вложенного класса?

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

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

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

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

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

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


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

4b9b3361

Ответ 1

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

ИЗМЕНИТЬ

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

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, это может сработать.

Ответ 2

Вы можете объявить внутри 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; }
    }
}

Ответ 3

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

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

    class N : IN
    {
        public int _field1;
    }
}

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

Ответ 4

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

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

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 через частный конструктор или getters/seters определенного поля,

Если вы не возражаете иметь дополнительную статическую функцию 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(), который затем назначает соответствующих делегатов, является гораздо более чистым и более универсальным решением, на мой взгляд. Альтернативой будет вызов рефлексии для каждого отдельного делегата, который вы, возможно, захотите создать, и даже тогда могут быть ограничения (например, невозможность создания делегатов для конструкторов).

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

Ответ 5

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

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