简体   繁体   中英

How to create a constructor that is only usable by a specific class. (C++ Friend equivalent in c#)

As far as I know, in C#, there is no support for the "friend" key word as in C++. Is there an alternative way to design a class that could achieve this same end result without resorting to the un-available "friend" key-word?

For those who don't already know, the Friend key word allows the programmer to specify that a member of class "X" can be accessed and used only by class "Y". But to any other class the member appears private so they cannot be accessed. Class "Y" does not have to inherit from class "X".

No, there is no way to do that in C#.

One common workaround is to based the object for which you want to hide the constructor on an interface. You can then use the other object to construct a private, nested class implementing that interface, and return it via a Factory. This prevents the outside world from constructing your object directly, since they only ever see and interact with the interface.

public interface IMyObject
{
     void DoSomething();
}

public class MyFriendClass
{
     IMyObject GetObject() { return new MyObject(); }

     class MyObject : IMyObject
     {
          public void DoSomething() { // ... Do something here
          }
     }
}

This is how I solved it. I'm not sure if it's the "right" way to do it, but it required minimal effort:

public abstract class X
{
    // "friend" member
    protected X()
    {
    }

    // a bunch of stuff that I didn't feel like shadowing in an interface
}

public class Y
{
    private X _x;

    public Y()
    {
        _x = new ConstructibleX();
    }

    public X GetX()
    {
        return _x;
    }

    private class ConstructibleX : X
    {
        public ConstructibleX()
            : base()
        {}
    }
}

不。您拥有的最接近的是internal构造函数,或者是private构造函数和单独的工厂方法(可能是internal ,所以您没有节省太多)。

What about just having it explicity implement an interface that is only visible to a certain class?

Something like:

public void IFreindOfX.Foo() //This is a method in the class that's a 'friend' to class X.
{
   /* Do Stuff */
}

and then make sure IFriendOfX is visible to class X. In your X class you'd call the method by first casting X to IFriendOfX then calling Foo(). Another advantage is that is is fairly self documenting... that is, it's pretty close to having the friend keyword itself.

What about creating a private class? This does exactly what you seem to be describing. A member of class X can be accessed and used only by class Y , and to any other class it appears private, since, well, it is private:

public class Y
{
   private class X { }

   private X Friend;

   public Y()
   {
      Friend = new X();
   }
}

As far as I know, the Internal keyword is the closest thing in .NET. This question will shed more light on Internal: Internal in C#

As a workaround, I suppose you could create a conditional in your constructor that uses reflection.

For example, if Class1's constructor must be called by Class2:

public Class1()
{
    string callingClass = new StackFrame(1).GetMethod().DeclaringType.Name;

    if (callingClass != "Class2")
    {
        throw new ApplicationException(
            string.Concat("Class1 constructor can not be called by ",
            callingClass, "."));
    }
}

EDIT:

Please note that I would never actually do this in "real" code. Technically it works, but it's pretty nasty. I just thought it was creative. :)

The only thing I can think of that would even come close would be protected internal but that does not restrict it to a specific class. The only friending I'm aware of in c# is to make a friend assembly. Still does not restrict to a specific class.

The only thing I could think of to try and do it would be to do something like the following:

public class A
{
   public A() {}
   protected internal A(B b) {}
}

public class B
{
   A myVersion;

   public B() 
   {
      myVersion = A(this);
   }
}

The only other way I could think of would be to do some sort of Constructor Injection using reflection that is done inside of your friend class. The injection mechanism would allow you to limit it to what you want but could be very cumbersome. Take a look at something like Spring.Net for some injection capabilities.

You can access private members/methods using Reflection.

Since it's got the design tag, I never particularly liked the friend keyword. It pierces encapsulation and that always felt dirty to me.

This has a bit of a smell. There are other plenty of other ways to achieve implementation hiding in C#. Limiting construction to only specific classes does not achieve all that much.

Could you please provide more information as to the purpose of this requirement? As already answered, internal is the closest match for limiting accessibility to the class. There are ways to build on top of that depending on the purpose.

You may employ abstract factory pattern and make the classes heavily coupled ..

  • Code

     public abstract class OurBaseClass { } public abstract class AbstractFactory { protected abstract OurBaseClass Create(String value); public void MethodInstantiatesAndConsumesMyClass() { var x = Create("This is mine") as MyClass; Console.WriteLine($"x.MyProperty={x.MyProperty}"); } public void MethodInstantiatesAndConsumesYourClass() { var x = Create("This is your") as YourClass; Console.WriteLine($"x.YourProperty={x.YourProperty}"); } } public class MyClass:OurBaseClass { public class MyClassFactory:AbstractFactory { protected override OurBaseClass Create(string value) { return new MyClass(value); } } public String MyProperty { get; private set; } MyClass(String value) { this.MyProperty=value; } public static readonly AbstractFactory Factory = new MyClassFactory { }; } public class YourClass:OurBaseClass { public class YourClassFactory:AbstractFactory { protected override OurBaseClass Create(string value) { return new YourClass(value); } } public String YourProperty { get; private set; } YourClass(String value) { this.YourProperty=value; } public static readonly AbstractFactory Factory = new YourClassFactory { }; } 
  • Test

     [TestClass] public class MyTestClass { [TestMethod] public void TestMethod1() { AbstractFactory fac; fac=MyClass.Factory; fac.MethodInstantiatesAndConsumesMyClass(); fac=YourClass.Factory; fac.MethodInstantiatesAndConsumesYourClass(); } } 
  • Output

     x.MyProperty=This is mine x.YourProperty=This is your 

There are two points to note:

  1. The AbstractFactory.Create is protected abstract , that is, it exposes Create to the concrete factory classes only. Frankly, this is a special case that a factory class does not expose creation method publicly.

  2. The concrete factory classes are nested. For this, it makes the access of the private constructors of concrete product classes possible.

So AbstractFactory becomes your friend . Additionally, singleton pattern involves in the example but there's no big deal.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM