C#: Защищенные переменные внутри общего класса могут быть доступны из другого подкласса этого общего класса. Могу ли я предотвратить это?

Скажем, у меня есть универсальный класс Foo, который имеет защищенную переменную

public class Foo<T> 
{ 
    protected bool knowsFu; 
}

У меня также есть 2 подкласса: Бар и Труба

public class Bar : Foo<Bar> {}

public class Pipe : Foo<Pipe> {}

На самом деле я могу получить доступ к knowFu в Pipe FROM Bar, например:

public class Bar : Foo<Bar> 
{
    void UpdateFuInOtherClass(Pipe p)
    {
        p.knowsFu = false;
    }
}

Это намеренное поведение? (Если это так, какой будет вариант использования?)

Есть ли способ предотвратить изменение / достижение защищенной переменной другими текущими подклассами внутри моего текущего подкласса?

Более конкретно: я использую универсальный класс для реализации шаблона Singleton: https://en.wikipedia.org/wiki/Singleton_pattern

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

РЕДАКТИРОВАТЬ: Может быть уместно заметить, что защищенная переменная (knowFu) на самом деле также STATIC.

РЕДАКТИРОВАТЬ 2: Хорошо, может быть, пример был abit слишком универсальным... вот как я на самом деле в настоящее время реализую это:

зачем использовать синглтон? A: Платформа, над которой я работаю, - Unity3D, в которой часто используется шаблон

У меня есть типизированный абстрактный класс SingletonBehaviour

public abstract class SingletonBehaviour<T> where T : MonoBehaviour
{
    public static T Instance { get { return instance; } }

    protected static T instance { get; private set; } }

    // Loading is done through Unitys Awake-Method
}

Один из объектов Singleton, который я использую, - это APIManager.

public class APIManager : SingletonBehaviour<APIManager>
{
    // Methods like SendHTTPPost(), HTTPGet(), etc.
}

Однако, поскольку большинству моих проектов нужна лучшая реализация API, чем то, что я сейчас делаю:

public class ProjectAAPIManager : APIManager 
{
    // Overriding Instance so my return value is not APIManager but instead ProjectAAPIManager
    public static new ProjectAAPIMamager Instance { get { return (ProjectAAPIManager)instance; } }
}

Это ^ причина того, что моя (внутренняя) переменная экземпляра защищена, а не закрыта.

Однако из-за этого любой другой SingletonBehaviour в моем проекте теперь может получить доступ к (внутренней) переменной экземпляра в моем ProjectAAPIManager

public class GameController : SingletonBehaviour<GameController> 
{
    private void AMethod()
    {
         // Accessing inner variable instead of public one
         ProjectAAPIManager.instance.DoSomething();
    }
}

Поскольку это всего лишь добытчик, в настоящее время это не имеет большого значения. Но что, если мне понадобится доступ к сеттеру в моем подклассе?

Кроме того: стоило ли бы вообще печатать мой APIManager?

2 ответа

Ваш вопрос не что иное, как недоумение. Как сделать защищенный член недоступным из производного класса? Что ж, хорошее начало не делает его защищенным.

protected по определению именно то, что вы не хотите, так что не используйте его! Вместо этого используйте приватный.

Если вы спрашиваете, как сделать его доступным только для чтения при доступе из производных типов, у вас есть два варианта:

  1. Объявите это как только для чтения в базовом классе, если это возможно.
  2. Вместо этого используйте защищенное свойство с частным установщиком.

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

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

Вместо этого вы должны сделать что-то вроде этого:

class Program
{
    static void Main(string[] args)
    {
        var barInstance = Foo<Bar>.GetInstance();
    }
}

public class Foo<T> where T : new()
{
    protected bool knowsFu;

    private static T _instance;

    public static T GetInstance()
    {
        if (_instance == null)
            _instance = new T();

        return _instance;
    }
}

public class Bar
{
    public Bar()
    {
    }
}

Изменить 1:

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

public class SingletonBehaviour<T> where T : new()
{    
    public static T Instance 
    { 
        get 
        {
            if(instance == null)
                instance = new T()
            return instance; 
        } 
    } 
    private static T instance { get; set; } 
}

public class APIManager // This class should not inherit from the SingletonBehavior class
{
    // Methods like SendHTTPPost(), HTTPGet(), etc.
}

public class ProjectAAPIManager : APIManager 
{
    public ProjectAAPIManager GetInstance() => SingletonBehavior<ProjectAAPIManager>.Instance();
}
Другие вопросы по тегам