2014-09-22 5 views
0

Я знаю, что есть «внутреннее» ключевое слово и [InternalsVisibleTo]. Но как разрешить класс на уровне класса, который не находится в той же сборке для изменения личных данных? То есть, разрешить конкретному классу в сборке доступ к частным данным, но не к классам evey в этой сборке?Как реализовать отношения с C++ как отношения друзей в C# на уровне класса, но не с уровня сборки?

Я задал этот вопрос до этого How to implement C++ like friend relationship in C#, но он не является достаточно конкретным, поэтому я снова спрашиваю здесь. обсуждалось

Другое обсуждения, связанное с теорией и причиной, почему C# не использовать друг здесь Why does C# not provide the C++ style 'friend' keyword?

+3

Не. Придумайте лучший дизайн, кому нужны друзья? Это всегда было признаком вопиющего кода на C++. Как альтернативное предложение, возможно, подумайте об использовании интерфейсов и явной реализации http://msdn.microsoft.com/en-us/library/aa288461(v=vs.71).aspx – Mick

+0

@Mick Да, я полностью согласен с вами. Но я просто думаю об этом из любопытства и цели исследования/исследования, просто чтобы узнать, что мы можем получить от C#. –

+0

Я думаю, что Андерс Хейлсберг был бы полностью осведомлен о всех конструкциях, доступных на C++, и сделал преднамеренные решения о том, как помочь в продвижении хороших практик на C#, а также о том, какие конструкции на C++ удаляются из C# для устранения в плохих практиках, вероятно, попал в последнюю категорию. Это признак плохого сочетания дизайна, когда я сказал, что лучший дизайн я бы назвал менее тесно связанным дизайном. Ваш ответ ниже - иллюстрация чрезмерной связи. – Mick

ответ

0

Я думал об этом, и я думаю, что у меня есть решение, отражение. Не уверен, что это хороший способ сделать это.

Если у меня есть SomeClass, у которого есть FriendClass. NotFriendClass и FriendClass находятся в одной и той же сборке, но только FriendClass может получить доступ к частным данным SomeClass. Вот что нужно SomeClass:

class SomeClass 
{ 
    private bool isMyFriend() 
    { 
     StackTrace st = new StackTrace(); 

     StackFrame callerSF = st.GetFrame(2); 

     MethodBase mb = callerSF.GetMethod(); 
     Type callerType = mb.DeclaringType; 

     // FriendClass is my friend 
     if (typeof(FriendClass) == callerType) 
      return true; 
     else 
      return false; 

    } 
// .... 

В этом методе SomeClass проверяет, является ли класс вызывающего абонента его другом. Да, у него есть жестко закодированное if (typeof (FriendClass) == callerType), но дружке C++ также необходимо жестко указать имя класса в объявлении.

Для тех «другу-awared» методы SomeClass, она должна быть как:

public bool SetData(int x) 
    { 
     if (!isMyFriend()) 
      return false; 

     this.privateData = x; 
     return true; 

    } 

Единственная проблема в том, что проверка времени выполнения. Но все же, я думаю, что это достаточно хорошо для переноса некоторой программы с C++ на C# с помощью друга.

0

Я обычно переводил случаи, когда ключевое слово friend кажется удобным в шаблоне, где сам класс определяет, какие классы могут его создавать, не нарушая надлежащего OO-дизайна.

Идея состоит в том, что класс, который хочет контролировать, кто может создавать экземпляр, предлагает дружбу классам, которым разрешено получать экземпляр. Например, класс InControl позволяет классам ChosenOne и ChosenTwo получать экземпляры InControl.

Класс «избранный» (то есть ChosenOne или ChosenTwo) может «вовлечь» дружбу, предлагаемую InControl, с помощью экземпляра самой себя (friendship.engage (this)), и дружба, в свою очередь, может «принять» этот экземпляр, предоставив это экземпляр InControl (friend.accept (новый InControl())).

Настоящая дружба осуществляется с помощью вложенного единственного экземпляра класса Дружбы. Дружба ограничивается «выбранными» классами, используя общий интерфейс IFriendship, где T является выбранным классом. В свою очередь, каждому «выбранному» классу необходимо внедрить общий интерфейс IFriendable с T, являющимся InControl, чтобы иметь возможность получить экземпляр InControl.

Класс InControl имеет частный конструктор, чтобы избежать создания экземпляра кто-либо еще, кроме друзьями:

public interface IFriendable<T> 
{ 
    void accept(T friend); 
} 

public interface IFriendship<T> 
{ 
    void engage(T friend); 
} 

public class ChosenOne : IFriendable<InControl> 
{ 
    private InControl _friend { get; set; } 

    private ChosenOne() 
    { 
     InControl.friendship.engage(this); 
    } 

    public void accept(InControl friend) 
    { 
     _friend = friend; 
    } 
} 

public class ChosenTwo : IFriendable<InControl> 
{ 
    private InControl _friend { get; set; } 

    private ChosenTwo() 
    { 
     InControl.friendship.engage(this); 
    } 

    public void accept(InControl friend) 
    { 
     _friend = friend; 
    } 
} 

public class InControl 
{ 
    public interface IFriendship : IFriendship<ChosenOne>, IFriendship<ChosenTwo> { } 

    public static IFriendship friendship { get { return Friendship.instance; } } 

    private class Friendship : IFriendship 
    { 
     static Friendship() 
     { 
     } 

     internal static readonly Friendship instance = new Friendship(); 

     public void engage(ChosenOne friend) 
     { 
      friend.accept(new InControl()); 
     } 

     public void engage(ChosenTwo friend) 
     { 
      friend.accept(new InControl()); 
     } 
    } 

    private InControl() 
    { 
    } 
}