【问题标题】:How to create a constructor that is only usable by a specific class. (C++ Friend equivalent in c#)如何创建只能由特定类使用的构造函数。 (C# 中的 C++ Friend 等效项)
【发布时间】:2011-01-02 06:27:55
【问题描述】:

据我所知,在 C# 中,不支持 C++ 中的“朋友”关键字。是否有另一种方法来设计一个可以在不使用不可用的“朋友”关键字的情况下实现相同最终结果的类?

对于那些还不知道的人,Friend 关键字允许程序员指定类“X”的成员只能由类“Y”访问和使用。但是对于任何其他类,该成员显示为私有,因此无法访问它们。 “Y”类不必继承“X”类。

【问题讨论】:

  • 另外...我想知道微软是否正在考虑将这样的功能放入.Net 4.0
  • 不,他们不是。 .NET 4 保留了与以前版本相同的可访问性修饰符。

标签: c# constructor


【解决方案1】:

不,在 C# 中没有办法做到这一点。

一种常见的解决方法是将要为其隐藏构造函数的对象基于接口。然后,您可以使用另一个对象来构造一个实现该接口的私有嵌套类,并通过工厂返回它。这可以防止外部世界直接构造您的对象,因为他们只能看到界面并与之交互。

public interface IMyObject
{
     void DoSomething();
}

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

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

【讨论】:

  • 这实际上似乎对我的目的非常有效。我必须在对象的位置和命名空间上做出一些妥协......但它确实实现了我想要做的,+1
  • 好吧,接口可以在不同的命名空间中,所以不应该有任何妥协。不幸的是,对象的放置(在代码中)确实按照这种模式发生了变化。
  • 如果您希望嵌套类在单独的文件中,您可以将MyFriendClass 声明为部分类
【解决方案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()
        {}
    }
}

【讨论】:

  • 我更喜欢这种方法而不是@ReedCopsey,因为这样我可以真正定义类。接口对我不起作用,因为我还需要定义字段。 +1,真的很整洁。
  • 巧妙地使用抽象类!
【解决方案3】:

没有。您拥有的最接近的是 internal 构造函数,或 private 构造函数和单独的工厂方法(可能是 internal,因此您没有节省多少)。

【讨论】:

    【解决方案4】:

    让它explicity实现一个只对某个类可见的接口怎么样?

    类似:

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

    然后确保 IFriendOfX 对 X 类可见。在您的 X 类中,您可以通过首先将 X 转换为 IFriendOfX 然后调用 Foo() 来调用该方法。另一个优点是相当自我记录......也就是说,它非常接近于拥有friend关键字本身。

    【讨论】:

    • 这仅适用于对象,不适用于构造函数,但我真的不明白为什么你需要这样做,除非你说的是只有一些状态该对象在被朋友使用时有效。在这种情况下,您真的想从相关类派生一个新的“Friendly”类,该类仅对“Friend”类可见。
    【解决方案5】:

    创建一个私有类怎么样?这正是您所描述的。类 X 的成员只能由类 Y 访问和使用,并且对于任何其他类来说它是私有的,因为它是私有的:

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

    【讨论】:

    • 我希望在不同的程序集中定义朋友类。但是,是否可以告诉私有类位于不同的命名空间中?
    【解决方案6】:

    据我所知,Internal 关键字是 .NET 中最接近的东西。这个问题将更深入地了解内部:Internal in C#

    【讨论】:

      【解决方案7】:

      我能想到的唯一可以接近的就是protected internal,但这并不将其限制为特定的类。我在 c# 中知道的唯一交友是结交朋友程序集。仍然不限于特定的类。

      我唯一能想到的尝试去做的就是做以下事情:

      public class A
      {
         public A() {}
         protected internal A(B b) {}
      }
      
      public class B
      {
         A myVersion;
      
         public B() 
         {
            myVersion = A(this);
         }
      }
      

      我能想到的唯一其他方法是使用在您的朋友类内部完成的反射进行某种构造函数注入。注入机制将允许您将其限制为您想要的,但可能非常麻烦。看看 Spring.Net 之类的东西,了解一些注入功能。

      【讨论】:

      • 请记住,“受保护的内部”是指受保护和内部的更宽松组合,而不是更宽松组合。
      【解决方案8】:

      作为一种解决方法,我想您可以在使用反射的构造函数中创建一个条件。

      例如,如果 Class1 的构造函数必须由 Class2 调用:

      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, "."));
          }
      }
      

      编辑:

      请注意,我永远不会在“真实”代码中真正做到这一点。从技术上讲,它有效,但它非常讨厌。我只是觉得这很有创意。 :)

      【讨论】:

        【解决方案9】:

        您可以使用反射访问私有成员/方法。

        由于它有设计标签,我从来没有特别喜欢朋友关键字。它会穿透封装,我总是觉得很脏。

        【讨论】:

          【解决方案10】:

          这个有点味道。还有很多其他方法可以在 C# 中实现实现隐藏。将构造限制为仅特定类并不能实现那么多。

          您能否提供有关此要求目的的更多信息?正如已经回答的那样,internal 是限制对类的可访问性的最接近的匹配项。根据目的,有多种方法可以在此基础上进行构建。

          【讨论】:

            猜你喜欢
            • 2013-04-25
            • 1970-01-01
            • 1970-01-01
            • 2016-05-10
            • 1970-01-01
            • 2023-03-07
            • 1970-01-01
            • 2021-04-08
            • 2017-08-10
            相关资源
            最近更新 更多