【问题标题】:Is it possible in C# to force a private function to be callable from the constructor only?在 C# 中是否可以强制私有函数只能从构造函数调用?
【发布时间】:2015-01-14 10:07:52
【问题描述】:

我发现在某些情况下,构造函数中有很多代码,或者一个类有两个或多个具有可比代码的构造函数。在这些情况下,我经常创建一个私有方法。前者提高可读性,后者防止重复代码。

在某些情况下,这会产生一个只能从构造函数调用的私有方法(无论出于何种原因)。有没有办法强制执行?我可以想象做这样的事情:

using System.Diagnostics;

public class Foo
{
  private bool _constructing = true;

  private Foo()
  {
    _constructing = false;
  }

  public Foo(string someString) : this()
  {
    // constructor-specific code
    Initialize();
  }

  public Foo(double someDouble) : this()
  {
    // constructor-specific code
    Initialize();
  }

  private void Initialize()
  {
    Debug.Assert(!_constructing, "Initialize method should only be called from constructor");

    // shared code
  }
}

但这感觉有点笨拙。谁有更好的建议?

编辑:将构造函数链接添加到示例;我的意思是在原始示例中使用它。

编辑:我认为我在最初的问题中遗漏了一点 - 虽然在某些情况下链接构造函数确实提供了解决方案,但链接代码总是在您链接的构造函数中的代码之前执行(顺便提一下, 这就是为什么上面的例子不起作用)。在某些情况下,您想要执行共享代码的某些部分,然后执行其他操作。我将添加另一个示例来反映这一点:

using System.Diagnostics;

public class Foo
{
  private bool _constructing = true;

  public Foo(string someString)
  {
    // constructor-specific pre-processing code
    Initialize();
    // constructor-specific post-processing code

    _constructing = false;
  }

  public Foo(double someDouble)
  {
    // constructor-specific pre-processing code
    Initialize();
    // constructor-specific post-processing code

    _constructing = false;
  }

  private void Initialize()
  {
    Debug.Assert(!_constructing, "Initialize method should only be called from constructor");

    // shared code
  }
}

【问题讨论】:

  • 您可以链接构造函数,这样就不需要单独的方法。这假设在不同的构造函数中发生了类似的事情......stackoverflow.com/questions/1814953/…
  • _constructing 标记为readonly。这样您就可以确保没有其他代码能够将其还原为true
  • 由于该方法是私有的,我不明白为什么您需要做任何事情,除了在方法旁边添加注释说它只能从构造函数中调用。
  • 使用构造函数链。如果您需要初始化 readonly 字段,则 Initialize 方法没有用。
  • 我实际上是想在示例中使用构造函数链接;忘记仔细检查我自己的示例代码 - 抱歉!

标签: c# constructor assert


【解决方案1】:

构造函数可以互相调用:

class Foo
{
    private Foo()
    {

    }

    public Foo(int value) : this()
    {

    }
}

我想,你可以使用这个功能。

【讨论】:

  • 哎呀——这实际上是我在我的例子中想要做的——但并不是我想要的。我将编辑示例
  • 编辑后:您需要将Initialize 方法转入另一个构造函数(具有唯一参数的私有),然后调用它。在您的情况下,似乎最好将其转换为无参数私有构造函数。
【解决方案2】:

您可以为此使用CallerMemberName。编译器将使用调用该方法的原始方法填充它。在这种情况下.ctor(构造函数):

public static class Program
{
    static void Main(string[] args)
    {
        A a = new A();
    }
}

class A
{
    public A()
    {
        B();
    }

    [MethodImplOptions.NoInlining]
    private void B([CallerMemberName] string caller = null)
    {
        if (caller == ".ctor")
        {
        }
    }
}

为了防止内联,您可以将MethodImplOptions.NoInlining 放在方法B 上。

【讨论】:

  • 你仍然可以调用B(".ctor");,但至少这会引起警告。
  • 但是在那种情况下我不能还是打电话给B(".ctor")吗? AFAIK CallerMemberName 仅在未提供可选参数时才填充...
  • 我刚刚尝试使用 CallerMemberName (这似乎是我正在寻找的)但不幸的是,我暂时坚持使用 .NET 4.0 ......这似乎已经在4.5,对吧?
  • 我将接受这个作为解决方案 - 虽然我实际上不能使用这种方式,但它似乎最接近我想要的 - 我要么必须开始使用 4.5,要么只是咬了咬牙……
【解决方案3】:

我建议你这样做:

public class Foo
{
    private Foo()
    {
        // private constructors can only be called from
        // within the class during constuction
    }

    public Foo(string someString) : this()
    {
    }

    public Foo(double someDouble) : this()
    {
    }
}

: this() 的使用只能在构造期间调用,虽然这不会强制您的公共构造函数调用 : this(),但您无法保证您的公共构造函数无论如何都会调用 Initialize()

【讨论】:

  • 这里有一个特别相关的情况:序列化。我经常想要一个可序列化类中的 Initialise() 函数,它可以由构造函数和反序列化器调用,它们都具有初始化实例内容的工作。能够指定允许此 Initialise() 函数修改只读字段会非常整洁。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
  • 2021-11-15
  • 2021-07-04
  • 1970-01-01
  • 1970-01-01
  • 2019-08-04
  • 1970-01-01
相关资源
最近更新 更多