【问题标题】:Accessing a Private Constructor from Outside the Class in C#在 C# 中从类外部访问私有构造函数
【发布时间】:2011-05-08 22:42:45
【问题描述】:

如果我定义一个具有私有默认构造函数和一个有参数的公共构造函数的类,我如何访问私有构造函数?

public class Bob
{
   public String Surname { get; set; }

   private Bob()
   { }

   public Bob(string surname)
   {
      Surname = surname;
   }
}

我可以通过类上的静态方法访问私有构造函数,如下所示:

public static Bob GetBob()
{
   return new Bob();
}

我认为我可以通过扩展方法访问私有构造函数,因为(根据我的理解)扩展方法被翻译成 似乎是静态方法在课堂上,但我不能:

static class Fred
{
   public static Bob Bobby(this Bob bob)
   {
      return new Bob();
   }
}

那么,如何访问私有构造函数?

谢谢


编辑:

我想这样做的原因是我想为我们的一个业务类创建测试,但不允许此类的使用者能够错误地实例化对象。我正在测试它,所以我知道(我希望!)在什么情况下测试会失败。我现在仍在测试 n00b,所以我的想法可能是也可能不是做事的“错误方式”。

我已经改变了我的测试策略,只是按照这个类的消费者的方式做事,即调用公共方法,如果公共方法没问题,假设私有方法没问题。我仍然更愿意测试私有方法,但我的老板 对可交付成果感到不满 :-(

【问题讨论】:

  • 幽默,你为什么要在你的类实现之外调用你的私有构造器?
  • 为什么是近距离投票?这仍然是一个有效的问题,即使不建议获取构造函数。
  • 同意 -- 仍然是一个有效的问题,为什么要关闭?
  • 回复:测试私有方法。我发现通过单独测试私有方法,我的测试比只测试公共方法更小更精确。我倾向于将“私有”变成“受保护”,并在测试库中编写一个特定的包装类,将它们公开为公共版本,而不是使用反射。
  • 是的——很多以前的开发者写的遗留代码。另外,我不得不说,所涉及的业务逻辑(零件交换销售)荒谬地复杂(一开始比我的博士学位更复杂)——但这就是我们客户的工作方式。

标签: c# constructor visibility default-constructor


【解决方案1】:

方法 Bobby 仍在另一个类中,称为 Fred。这就是为什么您不能访问 Bob 类的私有构造函数的原因。附加方法无法完成您尝试做的事情。即使它们可以附加到另一个类上,它们仍然在该类之外声明并遵循通常的范围/访问规则。

【讨论】:

  • 感谢您的意见。我加入了Bobby 以展示我所做的事情,这样没有人会浪费时间建议扩展方法。
【解决方案2】:

新答案(九年后)

Activator.CreateInstance 现在有几个重载允许您使用非公共构造函数:

Activator.CreateInstance(typeof(YourClass), true);

true = 使用非公共构造函数。

.

旧答案

默认构造函数是私有的是有原因的。开发者不会为了好玩而将其设为私有。

但是如果你仍然想使用默认构造函数,你可以通过反射来获得它。

var constructor = typeof(Bob).GetConstructor(BindingFlags.NonPublic|BindingFlags.Instance, null, new Type[0], null);
var instance = (Bob)constructor.Invoke(null);

编辑

我看到了您关于测试的评论。永远不要测试受保护或私有的方法/属性。如果您无法通过公共 API 测试这些方法/属性,您可能做错了。要么删除它们,要么重构类。

编辑 2

忘记绑定标志。

【讨论】:

  • 这不是一种黑客行为吗?绕过将构造函数设为私有的原因?
  • 是的。这就是我写 默认构造函数是私有的原因。开发者不会为了好玩而将其设为私有。
  • 谢谢。但是,GetConstructor 方法返回 null,因此代码会引发 NullReferenceException。 typeof(Bob).GetConstructors(BindingFlags.NonPublic); 返回一个空数组。
  • @Liviu M. - 是的,这显然是一个 hack,但这正是 OP 在他的问题中所要求的,因此这是正确的答案;)
  • 我不确定“从不测试私有方法”的评论。虽然我大体上同意,但如果他想 MOCK 此类以单独测试使用此类实例的对象怎么办?他可能希望保持方法私有,因为它是由实用程序静态方法创建的,但想模拟它。或者,更容易的是,api 写得不好,但你仍然想测试你的代码来处理 api,我有这样的情况
【解决方案3】:

您可以通过反射实例化该类型的实例。

【讨论】:

    【解决方案4】:

    有几种方法可以解决这个问题:

    一个:将构造函数公开。如果您需要从类外部访问它,为什么它是私有的(可能是您只想访问私有构造函数进行测试,在这种情况下这是一个有效的问题)。

    :使构造函数受保护,然后通过派生类访问:

    public class Bob
    {
        public String Surname { get; set; }
    
        protected Bob()
        { }
    
        public Bob(string surname)
        {
            Surname = surname;
        }
    }
    
    public class Fred : Bob
    {
        public Fred()
            : base()
        {
        }
    }
    

    :使用反射(如jgauffin所示)。

    【讨论】:

    • 正如您在问题的编辑中看到的那样,它用于测试,谢谢。并且格式仍然存在。
    • 在同一答案中使用列表时的代码格式错误。
    • 干杯——我会努力记住这一点。
    • 如果你想在你的库中允许访问,你可以将构造函数设为内部。
    【解决方案5】:

    除了@jgauffin 的回答,它告诉如何通过反射调用私有构造函数:

    是否可以更改私有构造函数的修饰符?

    您似乎正在代码中实现工厂模式。因此修饰符应该是internal

    public class Product
    {
       //others can't create instances directly outside the assembly
       internal Product() { }    
    }
    public class ProductProvider
    {
       //they can only get standardized products by the provider
       //while you have full control to Product class inside ProductProvider
       public static Product CreateProduct()
       {
           Product p = new Product();    
           //standardize the product
           return p;
       }  
    }
    

    扩展方法

    public static MyExt
    {
       public static void DoSomething(this Product p) { }
    }
    

    调用p.DoSomething() 实际上等于MyExt.DoSomething(p)。它不是将此方法放入类 Product 中。

    【讨论】:

    • 请注意——访问修饰符“internal”不是命名空间级别的限制,而是程序集级别的限制。见:msdn.microsoft.com/en-us/library/7c5ka91b.aspx。你也可以把静态工厂方法放在 Product 类本身中。
    【解决方案6】:
    public class demo
    {
       private demo()
        {
            Console.WriteLine("This is no parameter private constructor");
        }
        public demo(int a)
        {
            demo d = new demo('c');// u can call both private contstructors from here
            demo dd = new demo();
            Console.WriteLine("This is one parameter public constructor");
        }
        private demo(char a)
        {
            Console.WriteLine("This is one parameter public constructor::" + a);
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            demo obj = new demo(7);
            // demo obj = new demo();  // it will raise error
            Console.ReadLine();
        }
    }
    

    【讨论】:

    • 请评论您的解决方案
    【解决方案7】:

    如果您使用的是 dotnet core,您可以执行以下操作,甚至无需调整任何反射:

    YourCustomObject player = (YourCustomObject)Activator.CreateInstance(typeof(YourCustomObject),true);
    

    Activator 就在 System 命名空间中。

    【讨论】:

    • 有效,但仅适用于无参数构造函数。
    【解决方案8】:

    如果一个类只有私有构造函数而没有公共构造函数,则只能从该类中的嵌套类访问它。值得考虑的是不能派生具有私有默认构造函数的类这一事实。 因此,只有当类中的实例旨在仅在此类内的嵌套类中创建时,才将构造函数或构造函数声明为私有(在没有公共讲师的类中)。没有其他有效的地方可以实例化这个类。

    【讨论】:

    • 可以派生具有私有默认构造函数的类,要继承您必须提供至少一个受保护或公共构造函数。
    • 如果您可以对此添加一些清晰度,那将更加有益。
    猜你喜欢
    • 2021-08-27
    • 2011-12-29
    • 1970-01-01
    • 2014-12-29
    • 2014-05-31
    • 2016-08-04
    • 2017-09-10
    • 2021-04-17
    • 2022-07-22
    相关资源
    最近更新 更多