簡體   English   中英

C#等同於C ++好友關鍵字?

[英]C# equivalent to C++ friend keyword?

我是C#的新手,我有一個問題,在C ++中我通常會使用friend標識符。 現在我知道在C#中不存在friend關鍵字,但我對如何解決這個問題沒有任何經驗(除了將所有類變量設為公共屬性,如果可以的話我想避免使用)。

我有以下場景:

public class A 
{
    public string Info { get; set; }
    /* much more data */
}

public class B
{
    private A m_instanceOfA;

    public B(A a) { m_instanceOfA = a; }

    public Info { get return A.info; set A.Info  = value; }

    /* And some more data of its own*/
}

public class C
{
    private A m_instanceOfA;

    // I need a constructor of C, which needs to set C.m_instanceOfA
    // to the same value as b.m_instanceOfA.
    public C(B b) { m_instanceOfA = b.m_instanceOfA ; } // <--- Not allowed!

    /* And some more data of its own*/
}

有沒有其他聰明的方法,沒有公開B.m_instanceOfA ,給C訪問這個變量(只在構造函數中)?

您可以使用下面顯示的技巧在Bob上創建一個只能由Alice調用的方法FriendRecieveMessageFromAlice 如果沒有對私人成員進行反思,邪惡的階級Eve將無法調用該方法。

我很想知道其他人之前是否提出過這個或其他解決方案。 幾個月來我一直在尋找這個問題的解決方案,而且我從來沒有看到一個確保真正的friend語義,只要沒有使用反射(你幾乎可以繞過任何東西)。

愛麗絲和鮑勃

public interface IKey { }

public class Alice
{
    // Alice, Bob and Carol must only have private constructors, so only nested classes can subclass them.
    private Alice() { }
    public static Alice Create() { return new Alice(); }

    private class AlicePrivateKey : Alice, IKey { }

    public void PublicSendMessageToBob() {
        Bob.Create().FriendRecieveMessageFromAlice<AlicePrivateKey>(42);
    }

    public void FriendRecieveMessageFromBob<TKey>(int message) where TKey : Bob, IKey {
        System.Console.WriteLine("Alice: I recieved message {0} from my friend Bob.", message);
    }
}

public class Bob
{
    private Bob() { }
    public static Bob Create() { return new Bob(); }

    private class BobPrivateKey : Bob, IKey { }

    public void PublicSendMessageToAlice() {
        Alice.Create().FriendRecieveMessageFromBob<BobPrivateKey>(1337);
    }

    public void FriendRecieveMessageFromAlice<TKey>(int message) where TKey : Alice, IKey {
        System.Console.WriteLine("Bob: I recieved message {0} from my friend Alice.", message);
    }
}

class Program
{
    static void Main(string[] args) {
        Alice.Create().PublicSendMessageToBob();
        Bob.Create().PublicSendMessageToAlice();
    }
}

前夕

public class Eve
{
    // Eve can't write that, it won't compile:
    // 'Alice.Alice()' is inaccessible due to its protection level
    private class EvePrivateKey : Alice, IKey { }

    public void PublicSendMesssageToBob() {
        // Eve can't write that either:
        // 'Alice.AlicePrivateKey' is inaccessible due to its protection level
        Bob.Create().FriendRecieveMessageFromAlice<Alice.AlicePrivateKey>(42);
    }
}

這個怎么運作

訣竅是方法Bob.FriendRecieveMessageFromAlice需要一個(虛擬)泛型類型參數作為令牌。 該泛型類型必須從Alice和虛擬接口IKey

由於Alice本身並沒有實現IKey ,因此調用者需要提供一些實現IKeyAlice子類。 但是, Alice只有私有構造函數,因此它只能由嵌套類子類化,而不能由其他地方聲明的類子類化。

這意味着只有嵌套在Alice中的類才能將其子類化以實現IKey 這就是AlicePrivateKey所做的,並且因為它被聲明為私有,所以只有Alice可以將它作為泛型參數傳遞給Bob.FriendRecieveMessageFromAlice ,因此只有Alice可以調用該方法。

然后我們以相反的方式做同樣的事情,這樣只有Bob才能調用Alice.FriendRecieveMessageFromBob

泄漏鑰匙

值得注意的是,在調用時, Bob.FriendRecieveMessageFromAlice可以訪問TKey泛型類型參數,並且可以使用它來欺騙Alice對另一個方法的調用OtherClass.OtherMethod<OtherTkey>接受OtherTKey : Alice, IKey 因此,使密鑰從不同的接口繼承是更安全的: Alice, IBobKey用於第一個, Alice, IOtherKey用於第二個。

比C ++朋友好

  • 即使Bob本身也無法調用自己的方法Bob.FriendRecieveMessageFromAlice
  • Bob可以擁有多個擁有不同朋友方法的朋友:

     // Can only be called by Alice, not by Carol or Bob itself Bob.FriendRecieveMessageFromAlice <TKey>(int message) where TKey : Alice, IKey { } // Can only be called by Carol, not by Alice or Bob itself Bob.FriendRecieveMessageFromCarol <TKey>(int message) where TKey : Carol, IKey { } 

我有興趣知道是否有某種方法可以比蠻力的試驗和錯誤更有效地找到這樣的技巧。 某種“C#類型系統的代數”,它告訴我們可以強制執行哪些限制,哪些不可以,但我沒有看到任何關於這類主題的討論。

內部

您可以使用internal關鍵字。 然后,您的類型(或類型成員)將僅對同一程序集中的其他類型可見; 並且:

如果需要從其他程序集中看到內部類型,可以使用InternalsVisibleToAttribute 此屬性以整個程序集為目標,通常寫在AssemblyInfo.cs文件中。


PS:C#中不存在Friend關鍵字,但友誼的概念存在(與C ++中的不完全相同),它在MSDN的Friend Assemblies文章中有所描述。 另請注意, 在VB.NET中存在friend關鍵字 ,其行為與C#internal關鍵字完全相同。

我修改了你發布的代碼,所以它應該按照你想要的方式工作:

using System.Reflection;
using System.Diagnostics;

public class A 
{
    public string Info { get; set; }
    /* much more data */
}

public class B
{
    private A m_instanceOfA;
    public string Info { get; set; }

    public B(A a) => Info = a;

    private readonly ConstructorInfo friend = typeof(C).GetConstructor(new Type[] { typeof(B) });
    public A InstanceOfA
    {
        get
        {
            if (new StackFrame(1).GetMethod() != friend)
               throw new Exception("Call this property only inside the constructor of C");
            return this.m_instanceOfA;
        }
    }
}

public class C
{
    private A m_instanceOfA;

    // Only the constructor of C can set his m_instanceOfA
    // to the same value as b.m_instanceOfA.
    public C(B b)
    {
        Info = b.InstanceOfA; // Call the public property, not the private field. Now it is allowed and it will work too, because you call it inside the constructor of C. In Main method, for example, an exception will be thrown, if you try to get InstanceOfA there.
    }
}

我認為你正在尋找“內部”關鍵字 - 基本上只對同一個程序集中的類可見

或者你也可以這樣(原諒方法名稱!):

public interface IAmAFriendOfB {
   void DoSomethingWithA(A instanceOfA);
}

public class B {
    private A m_instanceOfA;

    public B(A a) { m_instanceOfA = a; }

    public void BeFriendlyWith(IAmAFriendOfB friend) {
       friend.DoSomethingWithA(m_instanceOfA);
    }

    // the rest of your class
}

public class C : IAmAFriendOfB {

    private A m_instanceOfA;

    public C(B b) {
        b.BeFriendlyWith(this);
    }

    void DoSomethingWithA(A instanceOfA) {
        m_instanceOfA = b.m_instanceOfA;
    }   
}

您只能使用5個輔助功能修改器:

公共訪問不受限制。

protected Access僅限於從包含類派生的包含類或類型。

內部訪問僅限於當前程序集。

protected internal Access僅限於從包含類派生的當前程序集或類型。

private Access僅限於包含類型。

這是使用具有private單例實例的internal類的另一種替代方法,它允許您微調哪些方法暴露給偽friend類。

using System;

namespace Test
{
    public class A 
    {
        public string Info { get; set; }
        /* much more data */
    }

    public class B
    {
        private A m_instanceOfA;

        public B(A a) { m_instanceOfA = a; }

        public string Info
        {
            get { return m_instanceOfA.Info; }
            set { m_instanceOfA.Info = value; }
        }

        // requires an instance of a private object, this establishes our pseudo-friendship
        internal A GetInstanceOfA(C.AGetter getter) { return getter.Get(m_instanceOfA); }

        /* And some more data of its own*/
    }

    public class C
    {
        private A m_instanceOfA;

        private static AGetter m_AGetter; // initialized before first use; not visible outside of C

        // class needs to be visible to B, actual instance does not (we call b.GetInstanceOfA from C)
        internal class AGetter
        {
            static AGetter() { m_AGetter = new AGetter(); } // initialize singleton

            private AGetter() { } // disallow instantiation except our private singleton in C

            public A Get(A a) { return a; } // force a NullReferenceException if calling b.GetInstanceOfA(null)
        }

        static C()
        {
            // ensure that m_AGetter is initialized
            System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(AGetter).TypeHandle);
        }

        public C(B b)
        {
            m_instanceOfA = b.GetInstanceOfA(m_AGetter);
        }

        public string Info
        {
            get { return m_instanceOfA.Info; }
            set { m_instanceOfA.Info = value; }
        }

        /* And some more data of its own*/
    }

    public class Test
    {
        public static void Main()
        {
            A a = new A();
            B b = new B(a);
            C c = new C(b);
            c.Info = "Hello World!";
            Console.WriteLine(a.Info);
        }
    }
}

現場演示

C.AGetter類不能在其自身之外進行實例化,因此C.m_AGetterprivatestatic )表示只能從C訪問的單例實例。 作為B.GetInstanceOfA需要的一個實例C.AGetter ,這使得外部的功能無用C 該函數標記為internal以最大限度地減少其暴露,但該論點也應作為一種自我文檔的形式,它不適合常用。

接口替代方案有可能使方法超出其預期范圍(例如,實現接口的類不應該訪問公開的方法),而這種方法可以防止這種情況。 friend訪問的反對者可能仍然反對它,但這使事情更接近預期的范圍。

暫無
暫無

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

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