C # equivalent to keyword C ++ friend?

I am new to C # and I have a problem for which in C ++ I usually used the friend identifier. Now I know that the friend keyword does not exist in C #, but I have no experience on how to get around this (except that all the public properties of the class variables that I want to avoid if possible).

I have the following script:

 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*/ } 

Is there any other smart way, without making B.m_instanceOfA public, to give C access to this variable (only in the constructor)?

+6
source share
6 answers

Interior

You can use the internal keyword. Your type (or type member) will be displayed only for other types inside the same assembly; And:

If you want your inner types to be visible from other assemblies , you can use InternalsVisibleToAttribute . This attribute is intended for the entire assembly and is usually written to the AssemblyInfo.cs file.


PS: the Friend keyword does not exist in C #, but there is a concept of friendship (not quite the same as in C ++), it is described on the MSDN Friends Collection . Also note that the friend keyword exists in VB.NET and has the same behavior as the C # internal keyword.

+7
source

You can use the trick shown below to create a FriendRecieveMessageFromAlice method in Bob that can only be called by Alice . The evil class, Eve , cannot invoke this method without using reflection for private members.

I am curious to know if this or another solution was previously proposed by other people. I searched for a solution to this problem for several months, and I never saw what provided real semantics for friend , provided that reflection is not used (you can get around almost anything with it).

Alice and Bob

 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(); } } 

Eve

 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); } } 

How it works

The trick is that the Bob.FriendRecieveMessageFromAlice method requires a parameter (dummy) of a generic type that serves as a token. This generic type should inherit from both Alice and the dummy IKey interface.

Since Alice does not implement IKey itself, the caller needs to provide some subclass of Alice that implements IKey . However, Alice has only private constructors, so it can only be subclassed by nested classes, not classes declared elsewhere.

This means that only a class nested in Alice can subclass it to implement IKey . What makes AlicePrivateKey , and since it is declared private, only Alice can pass it as a general argument to Bob.FriendRecieveMessageFromAlice , so only Alice can call this method.

Then we do the same thing on the contrary, so that only Bob can call Alice.FriendRecieveMessageFromBob .

Key leak

It should be noted that when calling Bob.FriendRecieveMessageFromAlice has access to the generic type TKey and can use it to substitute a call from Alice using another method OtherClass.OtherMethod<OtherTkey> that accepts OtherTKey : Alice, IKey . Therefore, it would be safer to make keys inherited from different interfaces: Alice, IBobKey for the first and Alice, IOtherKey for the second.

Better than C ++ friend

  • Even Bob himself cannot call his own Bob.FriendRecieveMessageFromAlice method.
  • Bob can have several friends with different friends methods:

     // 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 { } 

I would be interested to know if there is a way to find such tricks in a more efficient way than the trial version and the error. Some kind of "algebra of a system like C #", which tells us what restrictions can be applied and what cannot, but I have not seen any discussions on this topic.

+10
source

You can use only 5 accessibility modifiers:

public Access is not limited.

protected Access is restricted to the containing class or types derived from the containing class.

internal Access is limited to the current assembly.

secure internal Access is limited to the current assembly or types retrieved from the containing class.

Private Access is limited to the containing type.

+2
source

I changed the code you posted, so it should work the way you want:

 using System.Reflection; using System.Diagnostics; //Using two namespaces more above public class A { private string info; public string Info { get { return this.info; } set { this.info = value; } } //I like this writing better /* much more data */ } public class B { private A m_instanceOfA; public B(A a) { this.m_instanceOfA = a; //I will prefer to add 'this' to non-local variables. } public string Info //Added 'string' between 'public' and 'Info', because before it was error { get { return this.m_instanceOfA.Info; //Write the name of the field, not his type. Info is not static } set { this.m_instanceOfA.Info = value; //Same as above in get } //Added '{' and '}' in get and set, because they are methods after all } //Added the following code private readonly ConstructorInfo friend = typeof(C).GetConstructor(new Type[] { typeof(B) }); public A InstanceOfA { get { if (new StackFrame(1).GetMethod() != this.friend) throw new Exception("Call this property only inside the constructor of C"); return this.m_instanceOfA; } } /* And some more data of its own*/ } 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) { this.m_instanceOfA = 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. } /* And some more data of its own*/ } 
+2
source

I think you are looking for an “internal” keyword - basically only visible to classes in the same assembly

Alternatively, you could something like (sorry method names!):

 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; } } 
+1
source

Here's another alternative, using the internal class with a singleton private instance, which allows you to fine-tune which methods are exposed to the pseudo- friend class.

 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); } } } 

Live demo

The C.AGetter class cannot be created outside of itself, therefore C.m_AGetter (which is both private and static ) represents a singleton instance accessible only from C Since B.GetInstanceOfA requires an instance of C.AGetter , this makes a useless function outside of C The function is labeled internal to minimize its exposure, but the argument should also act as a form of self-documentation that is not intended for general use.

An alternative interface can jeopardize methods that go beyond their intended scope (for example, a class that implements an interface where it should not have access to public methods), while this approach prevents this. Hidden friend access options may still mind him, but this is much closer to the intended area.

+1
source

Source: https://habr.com/ru/post/955097/


All Articles