31

I want a particular method in one class to only be accessible by a particular class. For example:

public class A
{
  public void LimitedAccess() {}
  public void FullAccess() {}
}

public class B
{
  public void Func()
  {
     A a = new A();
     a.LimitedAccess();       // want to be able to call this only from class B
  }
} 

public class C
{
  public void Func()
  {
     A a = new A();
     a.FullAccess();           // want to be able to call this method
     a.LimitedAccess();        // but want this to fail compile
  }
} 

Is there is a keyword or attribute that I can use to enforce this?

UPDATE:

Due to existing system complexity and time constraints, I needed a low impact solution. And I wanted something to indicate at compile time that LimitedAccess() could not be used. I trust Jon Skeet's answer that exactly what I had asked for could not be done in C#.

The question and Jon's answer are good for those who may run across this later. And the fact that this design smells can hopefully veer anyone away for choosing something like this as a desired a solution.

As mentioned in a comment, the C# friend conversation is useful reading if you are trying to solve a similar situation.

As for my particular solution: "why would A contain B's logic" (asked by @sysexpand in comments). That's the rub. B.Func() was called throughout the system I'm working on, but it primarily operated on a singleton of A. So what I ended up doing was moving B's Func() into A and making A.LimitedAccess() private. There were a few other details to work around, as there always are, but I got a low impact solution that gave me compile-time errors on callers to A.LimitedAccess().

Thanks for the discussion.

Community
  • 1
  • 1
jltrem
  • 12,124
  • 4
  • 40
  • 50
  • Why would you want do this? –  May 15 '13 at 18:59
  • I have a large class `A` that is used extensively throughout a system. Class `B` has other dependencies but has a the standard/best way of using B.LimitedAccess(). I want to enforce using `B.Func()` whenever possible. – jltrem May 15 '13 at 19:07
  • see http://stackoverflow.com/questions/203616/why-does-c-sharp-not-provide-the-c-style-friend-keyword you should mark `LimitedAccess` as internal and catch abuses of it through code review. – Yaur May 15 '13 at 19:08
  • @Jos.Schlitz I'm afraid there's some kind of design bug in your code, I can't think of a single situation where this would be necessary to do. You mind sharing some of your actual code? –  May 15 '13 at 19:14
  • A dirty option is to inherit B from A and make LimitedAccess protected. If its semantically meaningless, dont. – nawfal May 15 '13 at 19:15
  • @MarkusMeskanen - yes, there is certainly a design flaw. But I can't do an extensive redesign right now. I have to take a low impact approach. – jltrem May 15 '13 at 19:17
  • @Jos.Schlitz No, I edited your quote so it reveals the actual problem you're encountering here. You shouldn't do what you're trying to do. And if you *really* need to do it, don't. Change something else so you no longer need to do it. To be honest, if you're gonna do this, you're simply stubborn and ignorant. –  May 15 '13 at 19:37
  • @MarkusMeskanen No need for namecalling. Jos, you should put the clarifications you posted in the comment (need low impact approach, can't redesign project, the 'why') into the question as caveats. They don't do any long term good as comments. – George Stocker May 15 '13 at 20:41
  • @GeorgeStocker Not gonna take my words back, but I apologize. I should've considered my words better. :) –  May 15 '13 at 20:51
  • A great example of a need for something like this would be in a project using the Repository Pattern. Your aggregates could have methods which are only accessible by your Repository classes. In other words, only classes inheriting from your Repository Interfaces could Add children directly, for example. I know that there are other solutions for this, but they all seem to include things like giving access to private members. – Frank Apr 07 '15 at 20:01

7 Answers7

26

No. The only thing you could do would be to make LimitedAccess a private method, and nest class B within class A.

(I'm assuming you want all the classes in the same assembly. Otherwise you could put A and B in the same assembly, and C in a different assembly, and make LimitedAccess an internal method.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 2
    Actually there is another method, but this requires that the specific class creates the object and the created class expose a interface implementation as a out method ( `new A(out limitedAccessMethods)` ). Of course this requires a private nested class in `A` which implements the interface but allows access only by the creating class. However it's questionable if it's nice from a design point of view. And of course its limited. – Felix K. Oct 20 '15 at 20:34
7

Yes. What you are asking for is perfectly possible.

You can restrict access to methods and variables for a specific instance, by using an interface.

However, an interface alone cannot prevent someone from creating their own instance of the class, at which point they will have full access to that instance.

To do that, next you should nest it as a private class inside of another class in order to restrict access to the constructor.

Now you have a particular method in one class to only be accessible by a particular class.

In this example, only class B is ever able to access function LimitedAccess.

public interface IA
{
  void FullAccess();
}

public class B
{
  private class A : IA
  {
    public void LimitedAccess() {}  //does not implement any interface
    public void FullAccess() {}     //implements interface
  } 
    
  private A a = new A();

  public IA GetA()
  {
    return (IA)a;
  }
  
  public void Func()
  {
     /* will be able to call LimitedAccess only from class B, 
        as long as everybody else only has a reference to the interface (IA). */
     a.LimitedAccess();       
  }
} 


//This represents all other classes
public class C
{  
  public void Func(IA ia)
  {
     ia.FullAccess();           // will be able to call this method
     ia.LimitedAccess();        // this will fail to compile
  }
} 

public static class MainClass
{
  public static void Main(string[] args)
  {
    B b = new B();
    b.Func();
          
    IA ia = b.GetA();
      
    C c = new C();
    c.Func(ia);
  }
}
cowlinator
  • 7,195
  • 6
  • 41
  • 61
3

In case you just want to remind yourself (or team mates) to not call LimitedAccess everywhere, you could consider using explicit interface implementation or mark LimitedAccess as obsolete.

public interface IA
{
    void LimitedAccess();
    void FullAccess();
}

public class A : IA
{
    private void LimitedAccess() { }
    public void FullAccess() { }

    void IA.LimitedAccess() => LimitedAccess();
    void IA.FullAccess() => FullAccess();
}

public class B
{
    public void Func()
    {
        IA a = new A();
        a.LimitedAccess();       // want to be able to call this only from class B
    }
}

public class C
{
    public void Func()
    {
        A a = new A();
        a.FullAccess();           // want to be able to call this method
        a.LimitedAccess();        // -> fails to compile
    }
}
sa.he
  • 1,391
  • 12
  • 25
1

Maybe this is a workaround.

Use the System.Runtime.CompilerServices and then you can either check the Name of the calling function and/or the file, in which the calling function is defined. If you have a class per file, the filename might be a substitude for the class name. Check it and block the call.

internal void MySecretFunction (string something,
  [CallerMemberName] string memberName = null,
  [CallerFilePath] string filePath = null,
  [CallerLineNumber] int lineNumber = 0) {
    if (!filePath.EndsWith(@"\goodClass.cs")) return;

    // else do something
}
be_mi
  • 529
  • 6
  • 21
1

You could always see the calling type with a StackTrace. Just note that when building in release mode, the call on the stack will get optimized, and its possible that the stack trace could return a completely different class, so just make sure to test it before you publish.

/// <summary>
/// Warning: Any class that calls this other than "B" will throw an exception.
/// </summary>
public void LimitedAccess()
{
     if (new System.Diagnostics.StackTrace().GetFrame(1).GetMethod().DeclaringType != typeof(B)) throw new Exception("Invalid Caller Type, B is only class able to call this method.");
}

Unfortunately you wont be able to know if its an error on compile time. Best you can do is throw an exception if it gets called, and add a comment warning people about it.

Addio
  • 89
  • 1
  • 6
0

It is against OOP best practices to make such a design. Methods of classes are not supposed to be protected from being called.

If your design requires control over calling a method, then control should be exercised by testing the arguments - caller which is authorized to make a call would "know" the magic word to pass as the argument.

Zoran Horvat
  • 10,924
  • 3
  • 31
  • 43
  • 3
    Jon has provided two entirely appropriate means of doing this. Your method, on the other hand, is absolutely the wrong way to go about this. You don't get any compile time support, it is easily broken into or hacked through the use of a decompiler, and the users of the class now have a confusing API method to look at that they're not supposed to actually use. – Servy May 15 '13 at 20:14
  • The question was this: "I want a particular method in one class to only be accessible by a particular class." You just don't try to do that... The other thing is to declare a method internal and then to access it from ALL classes in the assembly. But that was not the question. – Zoran Horvat May 15 '13 at 20:20
  • And this doesn't solve that problem; the method would still be accessible everywhere, it just wouldn't work properly if used just anywhere without the correct magic argument. Both of Jon's solutions involve actually restricting the accessibility of the method from those that shouldn't have access. – Servy May 15 '13 at 20:22
  • You're missing the point. Compiler can only test method visibility, but method visibility is universal - it is either visible to other classes or it is not visible. There is no such thing as being visible to ONE class. Yaur has got it right - do the code review if you think that method was called from an improper location in code. If you disagree, then please come up with the solution... You might try with nested classes etc. but that's just running away from the core problem - and problem is bad design. – Zoran Horvat May 15 '13 at 20:37
  • As I've said twice now, Jon has provided two solutions that can work. If the method really should only be called from one other class then that other class should likely be a nested class, which would allow this exact behavior. Depending on the context it may also be appropriate for it to just be internal; exposing an extended portion of a type's functionality via `internal` and the rest as `public` is a common technique. – Servy May 15 '13 at 20:40
  • Private nested class with public method is just a private method. internal doesn't save a method from being called from many classes. If you want a method to be called only from one class, then put it into that class and make it private! Example is misleading - it creates new A just to call its method - if you have an instance then call the method, why the indirection? There is something else that only B knows about, and that should be an external dependency and passed to A. Only B knows how to make that "something" and that's the solution - beyond compiler's powers, but a proper OOP design. – Zoran Horvat May 15 '13 at 21:10
  • Apparently you don't understand how this can be accomplished using a nested class. It most certainly can be. `public class A{private void Foo(){} public class B{ private void Bar(){new A().Foo();}}` `B` in that example is a *public* class that can access the private `Foo` method of `A`, but it is the only other class that can do so, which is exactly what the OP asked for. As for internal, that's the solution that is not technically what the OP asked for, but is potentially capable of solving the OP's problem. If it isn't an option, then he'll need to rely on nesting the type. – Servy May 15 '13 at 21:13
  • Your code makes A.Foo() a private method of public class B. That's exactly what I've said. And it's a bad design because the whole process is around some secret object that has been provided by B and required by A to operate - otherwise why all this? Tomorrow, there will be a class C with its different secret ingredient. At that time it will become quite obvious that this is bad design, because A is now going to have two nested classes. Two days later - three nested classes, and so on. And the bonus question - why would A contain B's logic? That violates principles of good design. – Zoran Horvat May 15 '13 at 21:31
  • You're assuming that `Foo` is never called within `A` or that it doesn't access any private instance data of `A`. It doesn't in my simple example, but it could. That's what makes it different from a private method in `B`. It is not stated that there will never be additional classes that also need to access the data. This isn't something you should be doing *often*, but that's different than saying it shouldn't be done *ever*. Yes, it means that `A` and `B` are tightly coupled. This is in fact clearly indicated by the fact that they are nested. Sometimes that's appropriate. – Servy May 15 '13 at 21:34
  • "why would A contain B's logic" -- that's the rub. B.Func() was called throughout the system I'm working on, but it primarily operated on a singleton of A. So what I ended up doing was moving B.Func() into A and making A.LimitedAccess() private. There were a few other details to work around, as there always are, but I got a low impact solution that gave me compile-time errors on callers to A.LimitedAccess(). Thanks for the discussion. Even if the design isn't good, the original question is fun to consider in theory. – jltrem May 15 '13 at 21:37
  • 2
    Guys, thanks for the opportunity to discuss this issue so deeply. I'm certainly going to continue thinking about this coding pattern. – Zoran Horvat May 15 '13 at 21:51
0

This is a variation of the solution suggested by @cowlinator using class AWithUnlimitedAccess derived from class A rather than class A implementing interface IA.

The result and the limitations are the same, but I like it better because (1) the limited access methods are defined inside its own class and (2) it's easier to add documentation comments.

public class A
{
    public void FullAccess() { }
}


public class AWithUnlimitedAccess : A
{
    public void LimitedAccess() { }
}


public class B
{
    private AWithUnlimitedAccess a = new AWithUnlimitedAccess();

    public A GetA()
    {
        return a;
    }

    public void Func()
    {
        a.FullAccess();
        a.LimitedAccess();
    }
}

// This represents all other classes
public class C
{
    public A A;

    public void Func()
    {
        A.FullAccess();
        A.LimitedAccess(); // this will fail compile
    }
}

public static class MainClass
{
    static void Main(string[] args)
    {
        B b = new B();
        b.Func();
        C c = new C();
        c.A = b.GetA();
        c.Func();
    }
}
stenci
  • 8,290
  • 14
  • 64
  • 104