Цепочка конструктора C # - изменение порядка исполнения - PullRequest
6 голосов
/ 25 марта 2011

Я хочу знать, как изменить порядок выполнения при построении цепочек конструкторов в C #. Единственные методы, которые я видел, требуют, чтобы цепочечный конструктор вызывался первым, вне текущего конструктора.

В частности, возьмем следующий пример:

public class Foo {
  private static Dictionary<string, Thing> ThingCache = new Dictionary<string, Thing>();
  private Thing myThing;

  public Foo(string name) {
    doSomeStuff();
    if (ThingCache.ContainsKey(name)) {
      myThing = ThingCache[name];
    } else {
      myThing = ExternalStaticFactory.GetThing(name);
      ThingCache.Add(name, myThing);
    }
    doSomeOtherStuff();
  }

  public Foo(Thing tmpThing) {
    doSomeStuff();
    myThing = tmpThing;
    doSomeOtherStuff();
  }
}

В идеале, я хотел бы уменьшить повторение кода, делая это (заметьте, я допускаю, что в этом надуманном примере не так много кода сохраняется, но я работаю с кодом, который принесет гораздо больше пользы. Я использую этот пример ясность):

public class Foo {
  private static Dictionary<string, Thing> ThingCache = new Dictionary<string, Thing>();
  private Thing myThing;

  public Foo(string name) {
    if (ThingCache.ContainsKey(name)) {
      this(ThingCache[name]);
    } else {
      this(ExternalStaticFactory.GetThing(name));
      ThingCache.Add(name, myThing);
    }
  }

  public Foo(Thing tmpThing) {
    doSomeStuff();
    myThing = tmpThing;
    doSomeOtherStuff();
  }
}

Это возможно в VB .Net, но C # не позволяет мне вызывать конструктор в середине другого конструктора - только в начале, используя синтаксис Foo (): this ().

Итак, мой вопрос: как можно управлять порядком вызова конструктора при построении цепочки конструкторов, а не с помощью синтаксиса двоеточия, который может сначала вызвать только другой конструктор?

Ответы [ 4 ]

8 голосов
/ 25 марта 2011

Вы не можете вызывать конструкторы внутри других конструкторов. Конструктор может связывать только другой конструктор непосредственно перед ним.

Однако, чтобы решить вашу конкретную проблему, вы можете создать приватный конструктор, который может принимать оба типа аргумента, и ваши два исходных конструктора просто сковывают этот цепочку, передавая нулевой для отсутствующего аргумента.

Это имеет преимущество перед вызовом частных методов инициализации, так как оно хорошо воспроизводится с полями readonly (т.е. оно все еще работает, если myThing является полем readonly):

public class Foo
{
    private static Dictionary<string, Thing> ThingCache =
        new Dictionary<string, Thing>();
    private Thing myThing;

    public Foo(string name)
        : this(null, name)
    {
    }

    public Foo(Thing tmpThing)
        : this(tmpThing, null)
    {
    }

    private Foo(Thing tmpThing, string name)
    {
        if (tmpThing == null && name == null)
        {
            throw new System.ArgumentException(
                "Either tmpThing or name must be non-null.");
        }

        doSomeStuff();
        if (tmpThing != null)
        {
            myThing = tmpThing;
        }
        else
        {
            if (ThingCache.ContainsKey(name))
            {
                myThing = ThingCache[name];
            }
            else
            {
                myThing = ExternalStaticFactory.GetThing(name);
                ThingCache.Add(name, myThing);
            }
        }
        doSomeOtherStuff();
    }
}

Вы также можете использовать именованные или необязательные аргументы, если вы используете C # 4.0:

http://msdn.microsoft.com/en-us/library/dd264739.aspx

7 голосов
/ 25 марта 2011

Я хочу знать, как изменить порядок выполнения при построении цепочек конструкторов в C #.

Вы не делаете. В C # такой функции нет.

Уже существует механизм вызова произвольного кода в произвольном порядке: создайте несколько методов и вызовите их в нужном вам порядке.

Вот моя статья на эту тему более подробно.

http://blogs.msdn.com/b/ericlippert/archive/2010/01/28/calling-constructors-in-arbitrary-places.aspx

Если вас интересует тема принципов конструирования цепочек конструктора, вы можете прочитать

http://blogs.msdn.com/b/ericlippert/archive/2008/02/15/why-do-initializers-run-in-the-opposite-order-as-constructors-part-one.aspx

и

http://blogs.msdn.com/b/ericlippert/archive/2008/02/18/why-do-initializers-run-in-the-opposite-order-as-constructors-part-two.aspx

1 голос
/ 25 марта 2011

Вы создаете приватные инициализационные методы внутри вашего класса, и ваша логика конструктора вызывает эти методы.

class Foo
{
 public Foo(string name) 
 {
   InitializeBefore();

   if (ThingCache.ContainsKey(name)) 
   {
      myThing = ThingCache[name];
   } else 
   {
     myThing = ExternalStaticFactory.GetThing(name);
     ThingCache.Add(name, myThing);
   }

   InitializeAfter();
 }

 public Foo(Thing tmpThing) 
 {
   InitializeBefore();
   myThing = tmpThing;
   InitializeAfter();
 }

 private void InitializeBefore() 
 {
   doSomeStuff();
   // and any other calls you want before 
 }

 private void InitializeAfter() 
 {
   doSomeOtherStuff();
   // and any other calls you want at the end of the constructor
 }

}
0 голосов
/ 25 марта 2011

Вы должны использовать метод.Самый простой перевод будет:

public class Foo {
  private static Dictionary<string, Thing> ThingCache = new Dictionary<string, Thing>();
  private Thing myThing;

  public Foo(string name) {
    if (ThingCache.ContainsKey(name)) {
      Init(ThingCache[name]);
    } else {
      Init(ExternalStaticFactory.GetThing(name));
      ThingCache.Add(name, myThing);
    }
  }

  public Foo(Thing tmpThing) {
    Init(tmpThing);
  }

  private void Init(Thing tmpThing) {
    doSomeStuff();
    myThing = tmpThing;
    doSomeOtherStuff();
  }
}

По крайней мере, если у вас нет полей только для чтения.Я должен признать, что синтаксис для вызова других конструкторов в произвольных местах имеет свои преимущества.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...