Как создать конструктор, который будет использоваться только конкретным классом. (C ++ Friend эквивалент в c #) - PullRequest
11 голосов
/ 07 января 2010

Насколько я знаю, в C # нет поддержки ключевого слова "друг", как в C ++. Есть ли альтернативный способ разработки класса, который мог бы достичь такого же конечного результата, не прибегая к недоступному ключевому слову «друг»?

Для тех, кто еще не знает, ключевое слово Friend позволяет программисту указать, что член класса "X" может быть доступен и использоваться только классом "Y". Но любому другому классу член кажется закрытым, поэтому к ним нельзя получить доступ. Класс "Y" не должен наследоваться от класса "X".

Ответы [ 11 ]

10 голосов
/ 07 января 2010

Нет, в C # нет способа сделать это.

Одним из распространенных обходных путей является создание объекта, для которого вы хотите скрыть конструктор в интерфейсе. Затем вы можете использовать другой объект для создания частного вложенного класса, реализующего этот интерфейс, и вернуть его через Factory. Это не позволяет внешнему миру создавать ваши объекты напрямую, поскольку они только видят интерфейс и взаимодействуют с ним.

public interface IMyObject
{
     void DoSomething();
}

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

     class MyObject : IMyObject
     {
          public void DoSomething() { // ... Do something here
          }
     }
}
3 голосов
/ 21 ноября 2012

Вот как я это решил. Я не уверен, что это «правильный» способ сделать это, но это потребовало минимальных усилий:

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()
        {}
    }
}
2 голосов
/ 07 января 2010

А как насчет создания частного класса? Это именно то, что вы, кажется, описываете. Член класса X может быть доступен и использоваться только классом Y, и для любого другого класса он кажется закрытым, поскольку, это private:

public class Y
{
   private class X { }

   private X Friend;

   public Y()
   {
      Friend = new X();
   }
}
2 голосов
/ 07 января 2010

Как насчет того, чтобы explicity реализовать интерфейс, видимый только определенному классу?

Что-то вроде:

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

и затем убедитесь, что IFriendOfX видим для класса X. В вашем классе X вы бы вызвали метод, сначала приведя X к IFriendOfX, а затем вызвав Foo (). Еще одним преимуществом является то, что он довольно самодокументируется ... то есть он довольно близок к наличию самого ключевого слова friend.

2 голосов
/ 07 января 2010

Нет. Самый близкий у вас конструктор internal или конструктор private и отдельный фабричный метод (вероятно, internal, так что вы сэкономили немного).

1 голос
/ 07 января 2010

Единственное, о чем я могу даже подумать, это protected internal, но это не ограничивает его конкретным классом. Единственный знакомый мне знакомый в c # - это сборка друга. Все еще не ограничивается определенным классом.

Единственное, что я мог придумать, это попытаться сделать что-то вроде следующего:

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

public class B
{
   A myVersion;

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

Единственный другой способ, которым я мог бы придумать, - это сделать что-то вроде Constructor Injection, используя отражение, которое делается внутри класса вашего друга. Механизм впрыска позволит вам ограничить его тем, что вы хотите, но может быть очень громоздким. Взгляните на что-то вроде Spring.Net и ознакомьтесь с некоторыми возможностями впрыска.

1 голос
/ 07 января 2010

В качестве обходного пути, я полагаю, вы могли бы создать в своем конструкторе условие, использующее отражение.

Например, если конструктор Class1 должен быть вызван 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:

Обратите внимание, что я бы никогда не сделал это в «реальном» коде. Технически это работает, но довольно неприятно. Я просто думал, что это было креативно. :)

1 голос
/ 07 января 2010

Насколько я знаю, ключевое слово Internal - самая близкая вещь в .NET. Этот вопрос будет пролить больше света на Внутренний: Внутренний в C #

0 голосов
/ 12 июня 2019

Вы можете использовать абстрактный шаблон фабрики и сделать классы тесно связанными ..

  • Код

    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 { };
    }
    
  • Тест

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

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

Следует отметить два момента:

  1. AbstractFactory.Create - это protected abstract, то есть он выставляет Create только для конкретных заводских классов. Честно говоря, это особый случай, когда фабричный класс не раскрывает метод создания публично.

  2. Бетонные фабричные классы являются вложенными. Для этого возможен доступ к частным конструкторам классов конкретных продуктов.

Так AbstractFactory становится вашим другом . Кроме того, Singleton Pattern включает в пример, но нет ничего сложного.

0 голосов
/ 07 января 2010

Это немного пахнет. Есть много других способов достижения скрытия реализации в C #. Ограничение конструкции только определенными классами не достигает всего этого.

Не могли бы вы предоставить больше информации относительно цели этого требования? Как уже отвечено, internal является наиболее близким соответствием для ограничения доступа к классу. В зависимости от цели есть способы построить это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...