How to imitate a “friend” in C #?

I need to implement a bit of the “friend” function in C ++ in C # and look for ideas.

Consider two very close classes: Node and NodePart. Nodes contain NodeParts, which are added through public AddPart () calls from other systems with parts that these systems create. Node must “reach” NodePart in order to make some very specific notifications that NodePart will propagate through separate virtual protected methods to any derived classes after some processing. (For those familiar with programming component-based game objects, this is the same.)

I would like NodePart to give Node a way to get these few notification methods, while avoiding any other types in the system. Node does not need to access other internal components of NodePart, just forward some private notifications.

Now, putting these classes in an assembly and using the “internal” one will obviously do the trick, but I'm looking for a better example than this. (I am not interested in creating new assemblies for each set of classes with which I would like to do this in the future.)

Besides reflection + invoke, which is bright and fragile, what other patterns can you come up with to solve this problem here? My common sense tells me that interfaces are part of the solution, but I can't think about how.

(Note: I have everything in order with security through obscurity. This system should not be 100% proven against misuse - enough to prevent people from doing what they should not. We are not building defibrillators here.)

Update:. Most of the answers below require multiple builds. As I mentioned above, I would really like to avoid this. I do not want to put one system on the assembly. We have enough things as they are, and I cannot go down the IL communication route due to our use of XAML. But thanks for the answers anyway. :)

Update 2: I came across in Linqpad and suggested several options based on the answers below. What do you like the worst and why?

Option 1: Deprecated

#pragma warning disable 612 // doc this public sealed class Node : NodePart.SystemAccess { #pragma warning restore 612 NodePart _part; public NodePart Part { get { return _part; } set { _part = value; NotifyAdded(_part); } } } public class NodePart { void NotifyAdded() { Console.WriteLine("Part added"); } [Obsolete] public class SystemAccess // doc this { protected void NotifyAdded(NodePart part) { part.NotifyAdded(); } } } 

Not bad. A little strange, but the strangeness is very limited. I am inclined to this, because it is much more compact than the next option.

Option 2: Access + Hack

 public sealed class Node { static readonly NodePart.ISystemAccess _systemAccess; static Node() { _systemAccess = (NodePart.ISystemAccess)typeof(NodePart) .GetNestedTypes(BindingFlags.NonPublic) .Single(t => t.Name == "SystemAccess") .GetConstructor(Type.EmptyTypes) .Invoke(null); } NodePart _part; public NodePart Part { get { return _part; } set { _part = value; _systemAccess.NotifyAdded(_part); } } } public class NodePart { void NotifyAdded() { Console.WriteLine("Part added"); } internal interface ISystemAccess { void NotifyAdded(NodePart part); } class SystemAccess : ISystemAccess { void ISystemAccess.NotifyAdded(NodePart part) { part.NotifyAdded(); } } } 

Hacka hacka hacka. In my original version there was no reflection + invoke and it would rely on SystemAccess, which would not be obvious. It may be good, but I kind of like a little extra security that I get here, although it probably is not needed.

+4
source share
6 answers

Make NodePart a nested class in Node, it will be able to access all of its private members.

If this is not possible (do not want a using Node ?), You can try partial classes. I do not know how this will work, because I have never used them in a more advanced way.

+3
source

Are there friends of the assembly , what do you want to do? They have been in the framework since 2.0, but I don’t think it was very well advertised.

Friend collections allow you to access internal methods in assembly B from assembly A, but the protected and private members are still hidden.

Not sure if this will help, but thought it was possible.

Oh, and one of the drawbacks is that you need a strong name for these assemblies.

+2
source

The only way I can use interfaces is to explicitly implement NodePart fields, which requires that any derived classes be disabled before the interface if they want to access them.

 interface INodePart { T SomeValue { get; } } class NodePart : INodePart { T INodePart.SomeValue { get; private set; } } class Node { void AddNodePart(NodePart np) { T val = (np as INodePart).SomeValue; //require downcast here. ... } } 

Note that you need to smooth even inside NodePart if you want to access Somevalue.

This, obviously, is not proof of a fool - anyone can evade if he wants, but this, of course, is a disappointment.

To add to the frustration, you can mark the interface with the [Obsolete] attribute and wrap the NodePart and Node classes inside a blocked warning block, which will mean that someone else trying to use INodePart will receive a warning, but your code will not.

 #pragma warning disable 612 class NodePart : INodePart { ... } #pragma warning restore 612 

You can set this strictly and add /warnaserror:612 to your build arguments, but it will break your code if you rely on any other code marked as Obsolete elsewhere. (Although you can enable / disable this for certain files, you may need to manually crack the .csproj file)

+2
source
+1
source

I believe that you can really make the interface internal, so the desired behavior can be encapsulated in the main assembly of your project, but will something else not see the behavior?

0
source

You could, if you wanted to, do it with the help of such attributes so that the cryptographic hash of any class "Friended" is stored in friend classes, which could access the friends class code at runtime using reflection, and have a public version of the method, which checks this hash every time it is called. I have not tried this, and I'm not sure if reflection gives you anything suitable for a hash, like binary bits of the friend class.

Or you could use .NET security in a similar way.

Just a brainstorm, they won’t be worth it if you can avoid it.

-1
source

All Articles