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

发布于 2024-08-09 23:04:55 字数 779 浏览 3 评论 0原文

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

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

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 类实现(尽管我的问题中有最后一个要求......)

Is it possible to specify that members of a nested class can be accessed by the enclosing class, but not other classes ?

Here's an illustration of the problem (of course my actual code is a bit more complex...) :

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; }
    }

    // ...
}

I would like to prevent client code from creating instances of JournalEntry, but Journal must be able to create them. If I make the constructor public, anyone can create instances... but if I make it private, Journal won't be able to !

Note that the JournalEntry class must be public, because I want to be able to expose existing entries to client code.

Any suggestion would be appreciated !


UPDATE: Thanks everyone for your input, I eventually went for the public IJournalEntry interface, implemented by a private JournalEntry class (despite the last requirement in my question...)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

淡写薰衣草的香 2024-08-16 23:04:55

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

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

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

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

Actually there is a complete and simple solution to this problem that doesn't involve modifying the client code or creating an interface.

This solution is actually faster than the interface-based solution for most cases, and easier to code.

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

  public class JournalEntry
  {
    static JournalEntry()
    {
      _newJournalEntry = value => new JournalEntry(value);
    }
    private JournalEntry(object value)
    {
      ...
且行且努力 2024-08-16 23:04:55

如果您的类不太复杂,您可以使用公开可见的接口并将实际实现类设为私有,或者您可以为 JornalEntry 类创建一个受保护的构造函数并拥有一个私有类< code>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 });

现在我想到了,另一种可能性是在包含类中使用私有委托,该委托是从内部类设置

public class Journal
{
    private static Func<object, JournalEntry> EntryFactory;
    public class JournalEntry
    {
        internal static void Initialize()
        {
            Journal.EntryFactory = CreateEntry;
        }
        private static JournalEntry CreateEntry(object value)
        {
            return new JournalEntry(value);
        }
        private JournalEntry(object value)
        {
            this.Timestamp = DateTime.Now;
            this.Value = value;
        }

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

    static Journal()
    {
        JournalEntry.Initialize();
    }
        
    static JournalEntry CreateEntry(object value)
    {
        return EntryFactory(value);
    }
}

的应该为您提供所需的可见性级别,而无需诉诸缓慢的反射或引入额外的类/接口

If your class is not too complex, you could either use an interface which is publicly visible and make the actual implementing class private, or you could make a protected constructor for the JornalEntry class and have a private class JornalEntryInstance derived from JornalEntry with a public constructor which is actually instantiated by your 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);
    }
}

If your actual class is too complex to do either of that and you can get away with the constructor being not completely invisible, you can make the constructor internal so it is only visible in the assembly.

If that too is infeasible, you can always make the constructor private and use reflection to call it from your journal class:

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

Now that I think about it, another possibility would use a private delegate in the containing class which is set from the inner class

public class Journal
{
    private static Func<object, JournalEntry> EntryFactory;
    public class JournalEntry
    {
        internal static void Initialize()
        {
            Journal.EntryFactory = CreateEntry;
        }
        private static JournalEntry CreateEntry(object value)
        {
            return new JournalEntry(value);
        }
        private JournalEntry(object value)
        {
            this.Timestamp = DateTime.Now;
            this.Value = value;
        }

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

    static Journal()
    {
        JournalEntry.Initialize();
    }
        
    static JournalEntry CreateEntry(object value)
    {
        return EntryFactory(value);
    }
}

This should give you your desired visibility levels without needing to resort on slow reflection or introducing additional classes / interfaces

燃情 2024-08-16 23:04:55

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

public class Journal
{
    private class JournalEntry
    {
    }
}

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

public interface IJournalEntry
{
}

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

    private class JournalEntry : IJournalEntry
    {
    }
}

Make JournalEntry a private nested type. Any public members will be visible only to the enclosing type.

public class Journal
{
    private class JournalEntry
    {
    }
}

If you need to make JournalEntry objects available to other classes, expose them via a public interface:

public interface IJournalEntry
{
}

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

    private class JournalEntry : IJournalEntry
    {
    }
}
流殇 2024-08-16 23:04:55

一种更简单的方法是仅使用内部构造函数,但通过提供只有合法调用者才能知道的引用来让调用者证明他们是谁(我们不需要关注非公共反射,因为如果调用者可以访问非公共反射,那么我们就已经输掉了这场战斗——他们可以直接访问私有构造函数);例如:

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);
    }
}

A simpler approach is to just use an internal constructor, but make the caller prove who they are by supplying a reference that only the legitimate caller could know (we don't need to be concerned about non-public reflection, because if the caller has access to non-public reflection then we've already lost the fight - they can access a private constructor directly); for example:

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);
    }
}
迟月 2024-08-16 23:04:55

在这种情况下,您可以:

  1. 将构造函数设置为内部构造函数 - 这会阻止程序集外部的构造函数创建新实例,或者...
  2. 重构 JournalEntry 类以使用公共接口并创建实际的 JournalEntry< /code> 类私有或内部。然后可以公开接口以供集合使用,同时隐藏实际的实现。

我在上面提到了内部作为有效的修饰符,但是根据您的要求,私有可能是更合适的选择。

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

In this case you could either:

  1. Make the constructor internal - this stops those outside this assembly creating new instances or...
  2. Refactor the JournalEntry class to use a public interface and make the actual JournalEntry class private or internal. The interface can then be exposed for collections while the actual implementation is hidden.

I mentioned internal as a valid modifier above however depending on your requirements, private may be the better suited alternative.

Edit: Sorry I mentioned private constructor but you've already dealt with this point in your question. My apologies for not reading it correctly!

踏月而来 2024-08-16 23:04:55

对于通用嵌套类=)

我知道这是一个老问题,并且它已经是一个公认的答案,但是对于那些可能有类似情况的谷歌游泳者来说,这个答案可能会提供一些帮助。

我遇到这个问题是因为我需要实现与OP相同的功能。对于我的第一个场景这个这个答案效果很好。尽管如此,我还需要公开一个嵌套的泛型类。问题是,如果不使自己的类通用,则无法公开具有开放泛型参数的委托类型字段(工厂字段),但显然这不是我们想要的,所以,这是我针对这种情况的解决方案:

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);
    }
}

我们有 < strong>Boo 作为具有私有构造函数的内部嵌套类,我们在父类上维护一个字典,其中包含利用动态优势的通用工厂。因此,每次调用 TestMeDude 时,Foo 都会搜索 T 的工厂是否已创建,如果没有,则调用嵌套类的静态构造函数来创建它。

测试:

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

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

    foo.TestMeDude<string>();

    Console.ReadLine();
}

输出为:

在此处输入图像描述

For generic nested class =)

I know this is an old question and it has already an accepted answer, nevertheless for those google swimmers who may have a similar scenario to mine this answer may provide some help.

I came across this question for I needed to implement the same feature as the OP. For my first scenario this and this answers worked just fine. Nevertheless I needed also to expose a nested generic class. The problem is that you can not expose a delegate type field (the factory field) with opened generic parameters without making your own class generic, but obviously this is not what we want, so, here is my solution for such scenario:

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);
    }
}

We have Boo as our internal nested class with a private constructor and we mantain on our parent class a dictionary with these generic factories taking advantage of dynamic. So, each time TestMeDude is called, Foo searches for whether the factory for T has already been created, if not it creates it calling nested class' static constructor.

Testing:

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

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

    foo.TestMeDude<string>();

    Console.ReadLine();
}

The output is:

enter image description here

放飞的风筝 2024-08-16 23:04:55

Grizzly 建议的解决方案确实使在其他地方创建嵌套类变得有点困难,但并非不可能,就像 Tim Pohlmann 写的那样,有人仍然可以继承它并使用继承类 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
        }

    }
}

这个解决方案不是我在日常工作中会做的事情,不是因为它会导致其他地方出现问题,而是因为它是非常规的(我以前从未见过它),所以它可能会给其他开发人员带来痛苦。

The solution Grizzly suggested does make it a bit hard to create the nested class somewhere else but not impossible,like Tim Pohlmann wrote someone can still inherit it and use the inheriting class ctor.

I'm taking advantage of the fact that nested class can access the container private properties, so the container asks nicely and the nested class gives access to the 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
        }

    }
}

This solution is not something I would do on a regular day on the job, not because it will lead to problems in other places but because it's unconventional (I've never seen it before) so it might cause other developers pain .

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文