如何将对嵌套类成员的访问限制为封闭类?

时间:2009-11-03 02:03:46

标签: c# nested-class access-levels

是否可以指定封闭类可以访问嵌套类的成员,而不能指定其他类?

以下是问题的说明(当然我的实际代码有点复杂......):

public class Journal
{
    public class JournalEntry
    {
        public JournalEntry(object value)
        {
            this.Timestamp = DateTime.Now;
            this.Value = value;
        }

        public DateTime Timestamp { get; private set; }
        public object Value { get; private set; }
    }

    // ...
}

我希望阻止客户端代码创建JournalEntry的实例,但Journal必须能够创建它们。如果我公开构造函数,任何人都可以创建实例......但是如果我将其设为私有,Journal将无法实现!

请注意,JournalEntry类必须是公共的,因为我希望能够将现有条目公开给客户端代码。

任何建议都将不胜感激!


更新:感谢大家的意见,我最终选择了公共IJournalEntry界面,由私人JournalEntry课程实施(尽管我的问题中有最后一项要求......)

7 个答案:

答案 0 :(得分:76)

实际上,这个问题有一个完整而简单的解决方案,不涉及修改客户端代码或创建接口。

对于大多数情况,此解决方案实际上比基于接口的解决方案更快,并且更容易编码。

public class Journal
{
  private static Func<object, JournalEntry> _newJournalEntry;

  public class JournalEntry
  {
    static JournalEntry()
    {
      _newJournalEntry = value => new JournalEntry(value);
    }
    private JournalEntry(object value)
    {
      ...

答案 1 :(得分:41)

如果您的类不是太复杂,您可以使用公开可见的接口并将实际的实现类设为私有,或者您可以为JornalEntry类创建受保护的构造函数并拥有私有类{{1从JornalEntryInstance派生的公共构造函数实际上由JornalEntry实例化。

Journal

如果您的实际类太复杂而无法执行其中任何一项,并且您可以使构造函数不完全不可见,则可以使构造函数内部使其仅在程序集中可见。

如果这也是不可行的,你总是可以将构造函数设为私有,并使用反射从日记类中调用它:

public class Journal
{
    public class JournalEntry
    {
        protected JournalEntry(object value)
        {
            this.Timestamp = DateTime.Now;
            this.Value = value;
        }

        public DateTime Timestamp { get; private set; }
        public object Value { get; private set; }
    }

    private class JournalEntryInstance: JournalEntry
    { 
        public JournalEntryInstance(object value): base(value)
        { }
    }
    JournalEntry CreateEntry(object value)
    {
        return new JournalEntryInstance(value);
    }
}

现在我考虑一下,另一种可能性是在内容类中设置的包含类中使用私有委托

typeof(object).GetConstructor(new Type[] { }).Invoke(new Object[] { value });

这可以为您提供所需的可见性级别,而无需采用慢速反射或引入其他类/接口

答案 2 :(得分:20)

JournalEntry设为私有嵌套类型。任何公共成员只对封闭类型可见。

public class Journal
{
    private class JournalEntry
    {
    }
}

如果您需要将JournalEntry个对象用于其他类,请通过公共接口公开它们:

public interface IJournalEntry
{
}

public class Journal
{
    public IEnumerable<IJournalEntry> Entries
    {
        get { ... }
    }

    private class JournalEntry : IJournalEntry
    {
    }
}

答案 3 :(得分:12)

更简单的方法是使用internal构造函数,但通过提供只有合法调用者可以知道的引用(我们不需要),让调用者证明他们是谁关注非公开反思,因为如果调用者可以访问非公开反映,那么我们已经失去了战斗 - 他们可以直接访问private构造函数);例如:

class Outer {
    // don't pass this reference outside of Outer
    private static readonly object token = new object();

    public sealed class Inner {
        // .ctor demands proof of who the caller is
        internal Inner(object token) {
            if (token != Outer.token) {
                throw new InvalidOperationException(
                    "Seriously, don't do that! Or I'll tell!");
            }
            // ...
        } 
    }

    // the outer-class is allowed to create instances...
    private static Inner Create() {
        return new Inner(token);
    }
}

答案 4 :(得分:3)

在这种情况下,您可以:

  1. 使构造函数内部 - 这将阻止此程序集外部创建新实例或...
  2. 重构JournalEntry类以使用公共接口,并将实际的JournalEntry类设为私有或内部。然后,可以在隐藏实际实现的同时公开接口以进行收集。
  3. 我上面提到内部是一个有效的修饰符,但是根据你的要求,private可能是更适合的选择。

    编辑:抱歉,我提到了私有构造函数,但您已经在问题中处理了这一点。我为没有正确阅读而道歉!

答案 5 :(得分:0)

对于通用嵌套类=)

我知道这是一个老问题,它已经被接受了答案,但对于那些可能有类似场景的谷歌游泳运动员来说,这个答案可能会提供一些帮助。

我遇到了这个问题,因为我需要实现与OP相同的功能。对于我的第一个方案thisthis,答案工作得很好。不过我还需要公开一个嵌套的泛型类。问题是您不能使用打开的通用参数公开委托类型字段(工厂字段)而不使您自己的类通用,但显然这不是我们想要的,所以,这是我的解决方案:

public class Foo
{
    private static readonly Dictionary<Type, dynamic> _factories = new Dictionary<Type, dynamic>();

    private static void AddFactory<T>(Func<Boo<T>> factory)
        => _factories[typeof(T)] = factory;

    public void TestMeDude<T>()
    {
        if (!_factories.TryGetValue(typeof(T), out var factory))
        {
            Console.WriteLine("Creating factory");
            RuntimeHelpers.RunClassConstructor(typeof(Boo<T>).TypeHandle);
            factory = _factories[typeof(T)];
        }
        else
        {
            Console.WriteLine("Factory previously created");
        }

        var boo = (Boo<T>)factory();
        boo.ToBeSure();
    }

    public class Boo<T>
    {
        static Boo() => AddFactory(() => new Boo<T>());

        private Boo() { }

        public void ToBeSure() => Console.WriteLine(typeof(T).Name);
    }
}

我们有 Boo 作为我们的私有构造函数的内部嵌套类,我们在父类中使用动态这些通用工厂的字典。因此,每次调用 TestMeDude 时,Foo都会搜索是否已经创建了T的工厂,如果没有,则会创建它调用嵌套类&#39;静态构造函数。

测试:

private static void Main()
{
    var foo = new Foo();

    foo.TestMeDude<string>();
    foo.TestMeDude<int>();
    foo.TestMeDude<Foo>();

    foo.TestMeDude<string>();

    Console.ReadLine();
}

输出结果为:

enter image description here

答案 6 :(得分:0)

Grizzly提出的解决方案确实使在其他地方创建嵌套类变得有些困难,但并非没有可能,就像Tim Pohlmann所写的那样,有人仍然可以继承它并使用继承类ctor。

我利用了一个事实,即嵌套类可以访问容器的私有属性,因此容器的请求很好,并且嵌套类可以访问ctor。

 public class AllowedToEmailFunc
{
    private static Func<long, EmailPermit> CreatePermit;

    public class EmailPermit
    {
        public static void AllowIssuingPermits()
        {
            IssuegPermit = (long userId) =>
            {
                return new EmailPermit(userId);
            };
        }

        public readonly long UserId;

        private EmailPermit(long userId) 
        {
            UserId = userId;
        }
    }

    static AllowedToEmailFunc()
    {
        EmailPermit.AllowIssuingPermits();
    }

    public static bool AllowedToEmail(UserAndConf user)
    {
        var canEmail = true; /// code checking if we can email the user
        if (canEmail)
        {
            return IssuegPermit(user.UserId);
        }
        else
        {
            return null
        }

    }
}

这种解决方案不是我在日常工作中会做的事情,不是因为它会在其他地方导致问题,而是因为它是非常规的(我从未见过),因此可能会使其他开发人员感到痛苦。