0

I have a particular case in my current project.

I have:

public class A
{
   // etc.
}

public class B
{
   // etc.

   private void HandleSomeEvent(object parameter)
   {
      // Etc.
   }

   protected void HandleSomeOtherEvent(object parameter)
   {
      // Etc.
   }
}

I want:

  1. A to be able to call the private method B.HandleSomeEvent, but no other class (but B) to be able to do that
  2. A to be able to call the protected method B.HandleSomeOtherEvent, but no other class (but B and B's derived classes) to be able to do that

Is that possible in C# ?

  1. If possible, how to do that?
  2. If not possible, what are the alternatives which can protect B as much as possible from tampering from, say, a class C in the same assembly?
paercebal
  • 81,378
  • 38
  • 130
  • 159

6 Answers6

1

It is possible by emitting an interface of A then separating it from a concrete implementation. Once you did it you can write something like this

interface IA { }

class A : IA
{
    public static IA New() { return new A(); }
    private A() { }

    private void UseB() 
    {  
        var b = new B();
        b.HandleSomeEvent(this, null);
    }
}

class B
{ 
    public void HandleSomeEvent(A onlyAccess, object parameter) { }
}

In spite of the fact that HandleSomeEvent() is public only A can access it, since no one else can possibly get a concrete instance of A class. It has a private constructor, while the factory New() method returns an interface.

See my article Friends and internal interface members at no cost with coding to interfaces for details.

Reuven Bass
  • 672
  • 1
  • 6
  • 16
  • +1. An interesting hack... but a hack it remains: It's too much verbose, and it hides the frienship behind a convoluted design. I'll read you article in detail later. – paercebal Jun 22 '12 at 19:14
  • @paercebal I don't agree it is a hack. Programing to an interface is not a hack, it is a way of coding. Since you adopt it, you get the friendship almost for granted. Separating an interface from its implementation is not verbose at all, it is a good programing style. By the way, that is what my article about. – Reuven Bass Jun 29 '12 at 13:22
  • The example was about a class, but should it be about a struct in C#, is the cost of the boxing of the struct through its interface worth it? I don't thing so. . . Also, what if I call the public `B.HandleSomeEvent` method passing "null" instead of an instance of A? Thus, at best (If HandleSomeEvent checks A is not null), what I have is a runtime friendship. . . Last but not least, there is a constraint there: all A constructors must be private (or protected?) which could or could not be possible. This is why I think this is a hack: It relies in a lot of unrelated constraints. – paercebal Jun 29 '12 at 14:18
  • As an alternative, look at the following answer: http://stackoverflow.com/a/5855379/14089 . It uses reflection, and thus, is ugly. And it offers only runtime friendship (which is still not enough as far as I am concerned, but, well...), but at least, there is no necessity of an interface declaration, nor private constructors. – paercebal Jun 29 '12 at 15:51
  • @paercebal I see your points. Regarding null access you are right. Protecting constructors are part of coding to interface in my opinion. But I won't try to convince you anymore, since your arguments are reasonable and based on your own experience. – Reuven Bass Jul 02 '12 at 20:49
1

Is that possible in C# ?

No, unless you use reflection: private and protected members are not accessible from other classes.

If not possible, what are the alternatives which can protect B as much as possible from tampering from, say, a class C in the same assembly?

You could make B a nested class inside A and make it private. Then you can safely increase the visibility of the two methods of B as only A will be able to call them (unless reflection is used).

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • My current solution is a variation of your own: I make A a nested class inside B, and thus, A has "public" access to all methods inside B, but this is a pain, and I have yet to foresee all the consequences. – paercebal Apr 30 '11 at 11:57
1

It is possible, but I'd usually just go with internal and assume that other classes in the assembly are well behaved.

You could for example pass delegates to these methods to class A which stores them in protected static fields/properties.

CodesInChaos
  • 106,488
  • 23
  • 218
  • 262
  • Assuming the other classes are well behaved is assuming I'm the only maintainer of the assembly, and that I never do mistakes. This is not a viable solution (as I indicated when I wrote I didn't want C from the same assembly to tamper with B) – paercebal Apr 30 '11 at 11:53
  • The delegates solution is an interesting one, though. +1 – paercebal Apr 30 '11 at 11:54
1

"2. If not possible, what are the alternatives which can protect B as much as possible from tampering from, say, a class C in the same assembly?"

Would checking the type info of the caller and throwing an exception work?

      [MethodImpl(MethodImplOptions.NoInlining)]  
      private void HandleSomeEvent(object parameter)  
      {  
            StackTrace stackTrace = new StackTrace();
            StackFrame stackFrame = stackTrace.GetFrame(1);
            MethodBase methodBase = stackFrame.GetMethod();

            if(methodBase.DeclaringType == typeof(ClassA)) // Okay.
            else if (methodBase.DeclaringType == typeof(ClassB)) // Okay.
            else throw new ApplicationException("Not Okay");
      }
bricklayer137
  • 374
  • 2
  • 10
  • +1. Wow... This is convoluted, but if this works, it will offer at least a runtime-friendship with no string attached. And as a bonus, it enables function-level friendship. Now, I'll have to test it because the "Inlining" part is bothering me: What if the A calls B which calls C. If B is friend of C, this is Ok. But if the B call is inlined, doesn't it means that the stack, as far as the runtime is concerned, is modified at runtime, and thus, the code above will detect "A calls C" instead of "A calls B calls C"? If yes, the your code won't work. Still, this is an interesting solution. :-) – paercebal Jun 29 '12 at 15:56
0
  1. No, it isn't (unless you use reflection, but that's just bad design)
  2. Inheritance. But even then you won't be able to call the private method. You should think about your design and improve it.

As your class A should be able to call protected methods on B, I figure it has some sort of relationship with it. Ask yourself if there is a "is-a" relationship, as in "A is a B". In this case, you can make A inherit from B:

public class A : B
{
}

Now, you are able to call protected methods on B. But please, don't just inherit, always ask yourself if the inheritance makes sense.

Femaref
  • 60,705
  • 7
  • 138
  • 176
0

You can mark both methods as internal:

internal void HandleSomeEvent(object parameter)
{
   // Etc.
}

protected internal void HandleSomeOtherEvent(object parameter)
{
   // Etc.
}

This makes the two methods visible to all classes in the same assembly (and also the second method visible to all classes derived from B).

There is no way to make a method visible to a specific other class. After all, you're the one who's controlling all classes in your assembly, so it's up to you to make sure that no other class than A calls the methods.

If you really need help from a tool with this, you could write an FxCop rule or create some kind of post-built action that checks for method calls from other classes than A.

dtb
  • 213,145
  • 36
  • 401
  • 431
  • My problem is that to make sure no one else will tamper with B, using your solution, I need to create an assembly with only A and B inside. This is not a viable solution (this is what I added I didn't want C from the same assembly to tamper with B) – paercebal Apr 30 '11 at 11:51
  • "you're the one who's controlling all classes in your assembly" : No, I'm not. I'm not the only developer on this project. Think this as an international team for 30 of 40 developers. My project is but a DLL someone will probably code inside in the next months. – paercebal Apr 30 '11 at 12:09
  • "If you really need help from a tool with this, you could write an FxCop rule" : What I need is help from the C# compiler. Using an external tool to forbid classes to access public or internal methods authorized by the C# compiler is not a viable solution. – paercebal Apr 30 '11 at 12:11
  • @paercebal: C# is the wrong language for your needs then. I guess there are some hacks to make it work (passing delegates or an unforgeable token), but I'd consider them ugly. My recommendation: give up control over who calls your classes - just let it happen. Or if the consequences are really that bad, rethink your design. – dtb Apr 30 '11 at 12:38