簡體   English   中英

如何從其他類訪問私有成員?

[英]How can I access private members from other classes?

我對 C# 並不陌生,但沒有 Java 那么多的經驗。 如您所知,在 Java 中,我們可以從外部類訪問所有私有成員。 所以我在 C# 中嘗試了同樣的事情,因為我有一些字段和方法需要僅從我的插件庫內部訪問,並且不希望它顯示給用戶。 一個簡單的例子可以是這樣的。

public static class StaticClass {

    public class InstanceClass {
        private int oldValue;
        public int Value;
    }

    public static void Backup(InstanceClass ic) {
        ic.oldValue = ic.Value;
    }

    public static void Restore(InstanceClass ic) {
        ic.Value    = ic.oldValue;
    }
}

如果我將字段 oldValue 公開,那么當最終用戶使用該插件時,它會變得一團糟並且看起來很臟。 它不必是內部類或某種特定形式。 我只想知道是否有任何方法可以僅由我控制或訪問來自同一程序集中其他靜態類的實例的私有成員。

為了僅允許在程序集中訪問,請使用internal修飾符。

public class InstanceClass {
        internal int oldValue;
        public int Value;
    }

這在 C# 中是不可能的。 容器類對嵌套類沒有特殊訪問權限。

您可以從嵌套類訪問容器的私有成員,但反之則不行。 您嘗試使用的模式根本沒有在 C# 中使用 - 它違反了成員可訪問性。 有一些技巧可以在 C# 上強制使用 Java 模式(使用反射或濫用接口),但它們僅此而已 - 技巧。

“最干凈”的方法可能如下所示:

public static class StaticClass 
{
  private interface IInstanceClassInternal
  {
    int OldValue { get; set; }
  }

  public sealed class InstanceClass : IInstanceClassInternal 
  {
    int IInstanceClassInternal.OldValue { get; set; }

    public int Value;
  }

  public static void Backup(InstanceClass ic)
  {
    ((IInstanceClassInternal)ic).OldValue = ic.Value;
  }

  public static void Restore(InstanceClass ic) 
  {
    ic.Value = ((IInstanceClassInternal)ic).OldValue;
  }
}

很明顯,您正在嘗試用 C# 編寫 Java - 模式、編碼風格......這可能是一個壞主意。 那些靜態方法應該是擴展方法。 “對象中的隱藏功能”並不完全符合 C# 的 OOP 概念——你的父母不應該自由訪問你的膽量,它應該只真正擁有與其他人相同的公共接口。 畢竟,這就是 LSP 的全部意義所在——這種緊密耦合對於任何可擴展性來說都是相當棘手的。 如果您希望StaticClassInstanceClass es privates StaticClass ,為什么首先將StaticClassInstanceClass分開? 只需使InstanceClass BackupRestore公共成員 - 甚至是接口的一部分(甚至可以通過顯式實現,如果您想對InstanceClass用戶“隱藏”它)。

您可以使用internal訪問修飾符,請參閱https://msdn.microsoft.com/en-us/library/ms173121.aspx

內部僅從組件內部可見

示例: https : //dotnetfiddle.net/FNavfE

你有沒有試過讓它“內部”? 它將在同一個 dll 中可用,但在外部 dll 中不可用。

public class InstanceClass {
    internal int oldValue;
    public int Value;
}

從技術上講,您可以使用反射(如果您堅持使用private字段和靜態類方法):

using System.Reflection;

... 

public static void Backup(InstanceClass ic) {
  if (null == ic)
    throw new ArgumentNullException("ic");

  ic.GetType()
    .GetField("oldValue", BindingFlags.NonPublic | BindingFlags.Instance)
    .SetValue(ic, ic.Value);
}

public static void Restore(InstanceClass ic) {
  if (null == ic)
    throw new ArgumentNullException("ic");

  ic.Value = (int) (ic.GetType()
    .GetField("oldValue", BindingFlags.NonPublic | BindingFlags.Instance)
    .GetValue(ic));
}

但是,更好的方法是將訪問修飾符從private更改為internal

public class InstanceClass {
    internal int oldValue;
    public int Value;
}

更好的解決方案是將BackupRestore方法移動到InstanceClass

public class InstanceClass {
    private int oldValue;
    public int Value;

    public void Backup() {
      oldValue = Value; 
    }

    public void Restore() {
      Value = oldValue; 
    }
}

這個字段oldValueStaticClassInstanceClass的實現細節。 InstanceClass成為StaticClass的實現細節,並將接口StaticClass.IInstance導出到外部客戶端:

public static class StaticClass {
     public interface IInstance {
         int Value { get; set; }
     }

     private class InstanceClass: IInstance {
         public int oldValue;
         public Value { get; set; }
     }

     // Static class becomes responsible for handing out `IInstance` objects
     public static IInstance GetInstance() {
         return new InstanceClass();
     }

     public static void Backup(IInstance i) {
         if (i is InstanceClass ic) {
             ic.oldValue = ic.Value;
         }
         else {
             throw new InvallidOperationException("Can only Backup IInstance objects that were created by GetInstance");
         }
     }

     public static void Restore(IInstance i) {
         if (I is InstanceClass ic)
         {
             ic.Value = ic.oldValue;
         }
         else {
             throw new InvallidOperationException("Can only Restore IInstance objects that were created by GetInstance");
         }
     }

此解決方案類似於Luaan提出的解決方案。 但不是使用接口導出私有數據,而是使用接口限制公開數據; 在我看來,這是一個更簡潔的設計,更少的驚喜。
它確實將Value從字段更改為屬性; 所以當你真的需要一個字段時,這種模式不起作用。

OP 示例中的靜態類使它有點尷尬並且有更好的解決方案,但想象一下在常規類中,也許在存儲庫中。 在存儲庫上工作,當設置存儲庫中項目的屬性並且不希望項目包含對存儲庫或存儲庫觀察者的引用時,應該通知觀察者,這導致我搜索“僅容器類可訪問的方法? ” 這讓我想到了這個問題

我打算解決它如下:

public class Repo
{
    public interface IItem
    {
        int Id { get; }
        string MyProperty { get; }
    }

    private class Item
    {
        public int Id { get; }
        public string MyProperty { get; private set; }

        public bool TrySetMyProperty(string newValue)
        {
            if (!Equals(MyProperty, newValue) &&
                IsPreconditionValid())
            {
                MyProperty = newValue;
                return true;
            }
            else
            {
                return false;
            }

            IsPreconditionValid() => true;
        }
    }


    public event EventHandler<EventArgs> OnChanged;

    private readonly ConcurrentDictionary<int, Item> items = new ConcurrentDictionary<int, Item>();

    public IItem GetOrCreateItemById(int id)
    {
        bool changed = false;
        IItem result = items.GetOrAdd(int, CreateItem);
        if (changed)
        {
            OnChanged?.Invoke(this, EventArgs.Empty);
        }
        return result;

        IItem CreateItem(int key)
        {
            changed = true;
            return new Item() { Id = key };
        }
    }

    public bool TrySetItemMyProperty(int id, string newValue)
    {
        if (items.TryGet(id, out Item i))
        {
            if (i.TrySetMyProperty(newValue))
            {
                OnChanged?.Invoke(this, EventArgs.Empty);
                return true;
            }
        }
        return false;
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM