Как создать конструктор, который можно использовать только для определенного класса. (Эквивалент друга С++ в С#)

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

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

Ответ 1

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

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

public interface IMyObject
{
     void DoSomething();
}

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

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

Ответ 2

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

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()
        {}
    }
}

Ответ 3

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

Ответ 4

Как просто с помощью этой экспликации реализовать интерфейс, который доступен только для определенного класса?

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

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

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

Ответ 5

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

public class Y
{
   private class X { }

   private X Friend;

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

Ответ 6

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

Ответ 7

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

Например, если конструктор Class1 должен быть вызван классом2:

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:

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

Ответ 8

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

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

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

public class B
{
   A myVersion;

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

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

Ответ 9

Вы можете получить доступ к частным членам/методам с помощью Reflection.

Поскольку он получил тег дизайна, мне особо не понравилось ключевое слово friend. Он пронзает инкапсуляцию и всегда чувствовал себя грязным.

Ответ 10

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

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

Ответ 11

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

  • Код

    public abstract class OurClassBase {
    }
    
    public abstract class AbstractFactory {
        protected abstract OurClassBase 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:OurClassBase {
        public class MyClassFactory:AbstractFactory {
            protected override OurClassBase 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:OurClassBase {
        public class YourClassFactory:AbstractFactory {
            protected override OurClassBase 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 становится твоим другом. Кроме того, в примере используется шаблон синглтона, но в этом нет ничего сложного.