Как преодолеть недостаток дружбы в C #?

0

Мне очень нравится дружеская особенность C++. Скажем, у нас есть Родитель и Ребенок. Детский ctor берет Родитель в качестве параметра:

Child::Child(Parent & parent)
{
    // Parent::RegisterChild is private
    parent.RegisterChild(this);
}

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

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

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


Изменить: В ответ на комментарии

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

Теги:
friend

2 ответа

3

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

public class Outer
{
    // Code outside the body of Outer cannot call this. But
    // the code in Nested *is* inside the body of Outer, so it okay.
    private void Foo()
    {
    }

    private class Nested
    {
        internal void Bar()
        {
            new Outer().Foo();
        }
    }
}

Это может быть полезно в некоторых случаях, но, очевидно, не является общей заменой концепции классов "друг" в C++.

Одна вещь, о которой нужно знать, это InternalsVisibleToAttribute, которая позволяет одной сборке обращаться к внутренним членам другого. Я понимаю, что в вашем случае вы действительно хотите более ограниченный доступ, чем internal, но [InternalsVisibleTo] по-прежнему стоит знать - это особенно полезно для тестирования.

0

Вот основная идея того, что я реализовал для своего проекта, и это отлично работает. Надеюсь, это сработает и для вас. Обратите внимание, что это обеспечивает дружбу только во время выполнения (что не очень здорово).

public interface IFriendKey { object Id {get; set;} }

class Friend<TFriend>
{
    protected void FriendAssert(IFriendKey key)
    {
        if ( key == null || key.Id == null || key.Id.GetType() != typeof(TFriend) )
            throw new Exception("No right to execute the called method.");
    }
}

class A : Friend<B>
{
    public void f(IFriendKey key)
    {
        FriendAssert(key);
        Console.WriteLine("ONLY class B can execute this method successfully, even though it is declared public.");
    }
}

class B
{
    private class AFriendKey : IFriendKey 
    {
        public object Id {get; set;}
    }

    IFriendKey Key { get { return new AFriendKey() {Id = this}; } }

    public void g()
    {
        new A().f(this.Key); 
    }
}

public class Test
{
    public static void Main()
    {
        new B().g();
    }
}

Демо-версия.

Надеюсь, это поможет.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню