2013-03-12 2 views
10

Примечание/Отказ от ответственности: после нескольких поисков, ближайшая вещь, которую я имею, я видел на этом посту, это сообщение на SO (Method chaining and the finishing problem), которое похоже на мой вопрос, но на самом деле не отвечает на него, но в любом случае, я надеюсь, что это не дублирующий вопрос.Обеспечение вызова для завершения цепочки методов

Что я делаю:

Я создал беглую interfaceas фасада поверх существующей базы лесозаготовительной для связки вызовов методы - так что мой синтаксис выглядит как это:

Logger.Debug().Message("Debug message!").WriteToLog(); 
Logger.Error().Message("An exception occured").Exception(ex).WriteToLog(); 

Я передаю внутренний объект из одного вызова метода на следующий объект, чтобы при последнем вызове (метод WriteToLog); сообщение записывается в файл журнала где-то.

Бит я думаю пахнет

В целях проверки (только тогда, когда приложение в режиме отладки), у меня есть свойство по классу контекста (весь объект мешка собственности), которая получает передается от вызов метода возвращаемому объекту до тех пор, пока цепь не завершится; он является логическим, а по умолчанию - false.

Это свойство оценивается в деструкторе класса контекста с помощью Debug.Assert, чтобы определить, вызывается ли окончательный метод для завершения цепочки, поэтому во время разработки могут быть отобраны ошибки регистрации. (свойство, код, который устанавливает свойство и сам деструктор, создается в контексте предпроцессорной директивы #if DEBUG, поэтому, если он построен в версии или если символ не существует, код будет не скомпилированы.)

I знаю с использованием деструктора плохо в C# 2.0 и выше и что у меня может не быть доступа к свойствам, потому что я считаю, что нет никаких гарантий относительно порядка завершения. Вот почему это происходит только при построении в режиме отладки и почему я хотел бы уйти от него.

Причины Я пытаюсь построить assertation в том, что это очень легко забыть и писать код как

Logger.Debug().Message("Debug message!"); 

, что означает, что ничто не регистрируется, хотя при беглом взгляде он выглядит должно.

Мой вопрос

То, что я хочу знать, - может кто-нибудь придумать другой способ проверки того, что последний метод всегда вызывается? Эти сообщения просто необходимы во время разработки, чтобы подчеркнуть разработчику, что цепочка методов еще не закончена. Я не хочу, чтобы конечные пользователи находили сообщения об ошибках, связанные с протоколированием в конечном продукте.

+0

+1 для элегантно представил/форматированный вопрос. –

+3

Почему у вас свободный API? что не так с помощью 'Logger.Debug (« Debug message! »);' и 'Logger.Error (« Исключение произошло », ex);'? Мне кажется, что вы делаете свой API регистрации сложнее, чем нужно ... Свободные API-интерфейсы - хорошая концепция, но они не являются серебряной пулей, не пытайтесь использовать их везде, потому что вы можете. –

+0

@TrevorPilley Поскольку это похоже на то, что у меня есть на данный момент, и это вызывает больше проблем, чем это делает. Существуют перегрузки для форматирования строк, принятия деталей исключения или того и другого, что приводит к тому, что разработчики забывают передать аргумент строкового формата или деталь исключения, которая заканчивается созданием ошибки. Последнее, что я хочу, - это диагностический инструмент, который выдает ошибку, поэтому я хочу свободный интерфейс, чтобы я мог быть явным о том, что передается где. Это не то, что я могу поменять, но, надеюсь, может поставить фасад. – Jay

ответ

9

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

Logger.Debug.Message("Test"); 

или даже просто:

Logger.Debug("Test"); 

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

Так вместо того, чтобы сделать это:

Method1().Method2().Method3(); 

и затем забыть окончательный вызов:

Method1().Method2().Method3().Execute(); 

вы бы вместо того, чтобы организовать код, возможно, как это:

Method1(o => o.Method2().Method3()); 

Для сделайте это, вы бы определили объект, на который вы назовете все текущие методы:

public class LoggerOptions 
{ 
    public LoggerOptions Debug() { LoggerType = LoggerType.Debug; return this; } 
    public LoggerOptions Error() { LoggerType = LoggerType.Error; return this; } 
    public LoggerOptions Message(string message) { ...; return this; } 

    public LoggerType Type { get; set; } 
    ... 
} 

Каждый вызов метода здесь изменит объект LoggerOptions и вернет тот же экземпляр назад, чтобы продолжить свободный интерфейс.

, а затем:

public static class Logger 
{ 
    public static void Log(Func<LoggerOptions, LoggerOptions> options) 
    { 
     LoggerOptions opts = options(new LoggerOptions()); 
     // do the logging, using properties/values from opts to guide you 
    } 
} 

Вы бы тогда называть это так:

Logger.Log(opts => opts.Debug().Message("Debug message")); 

Если у вас есть некоторые терминальные методы, которые вы абсолютно необходимо для вызова до завершения настройки параметров объекта, вы можете делают различные объекты:

public class LoggerOptions 
{ 
    public LoggerOptions Debug() { LoggerType = LoggerType.Debug; return this; } 
    public LoggerOptions Error() { LoggerType = LoggerType.Error; return this; } 
    public LoggerOptions Message(string message) { ...; return this; } 

    public LoggerType Type { get; set; } 
    ... 

    public LoggerFinalOptions ToEventLog() { ...; return new LoggerFinalOptions(this); } 
    public LoggerFinalOptions ToFile(string fileName) { ...; return new LoggerFinalOptions(this); } 
} 

, а затем:

public static class Logger 
{ 
    public static void Log(Func<LoggerOptions, LoggerFinalOptions> options) 
    { 
     LoggerFinalOptions opts = options(new LoggerOptions()); 
     // do the logging, using properties/values from opts to guide you 
    } 
} 

Это позволило бы гарантировать, что вы не можете скомпилировать код, не разрывая цепочку методов с вызовом то, что возвращает явные конечные параметры объекта:

// will not compile 
Logger.Log(opts => opts.Debug().Message("Test")); 

// will compile 
Logger.Log(opts => opts.Debug().Message("Test").ToFile("log.log")); 
+0

Отличный ответ, просто протестированный, и это именно то, что я искал. С небольшой модификацией создания сигнатуры метода Log (Expression > expr) Я могу получить проверенный свободный интерфейс, который только компилируется (так что он имеет значение только для форматирования строк), если включен режим ведения журнала. Благодаря! – Jay