मैंने बहुत बार इस समस्या का सामना किया है और मुझे लगता है कि मैं एक सरल समाधान के साथ आया हूं।
प्रारंभ में मैं डेकोरेटर पैटर्न के साथ गया और मैन्युअल रूप से प्रत्येक विधि को लागू किया, जब आपके पास सैकड़ों विधियां हैं तो यह बहुत थकाऊ हो जाता है।
मैंने तब PostSharp का उपयोग करने का निर्णय लिया, लेकिन मुझे ऐसा कुछ करने के लिए एक संपूर्ण पुस्तकालय शामिल करने का विचार पसंद नहीं आया, जिसे मैं (बहुत सारे) सरल कोड के साथ पूरा कर सकूं।
मैं फिर पारदर्शी प्रॉक्सी मार्ग पर चला गया जो मज़ेदार था लेकिन इसमें गतिशील रूप से आईएल को चलाने के समय शामिल किया गया था और ऐसा कुछ नहीं होगा जो मैं उत्पादन वातावरण में करना चाहता हूं।
मैंने हाल ही में डिज़ाइन समय पर डेकोरेटर पैटर्न को स्वचालित रूप से लागू करने के लिए टी 4 टेम्प्लेट का उपयोग करने का निर्णय लिया है, यह पता चला है कि टी 4 टेम्प्लेट वास्तव में साथ काम करने के लिए काफी कठिन हैं और मुझे इसकी आवश्यकता है इसलिए मैंने कोड बेलो बनाया। यह त्वरित और गंदा है (और यह गुणों का समर्थन नहीं करता है) लेकिन उम्मीद है कि कोई इसे उपयोगी पाएगा।
यहाँ कोड है:
var linesToUse = code.Split(Environment.NewLine.ToCharArray()).Where(l => !string.IsNullOrWhiteSpace(l));
string classLine = linesToUse.First();
// Remove the first line this is just the class declaration, also remove its closing brace
linesToUse = linesToUse.Skip(1).Take(linesToUse.Count() - 2);
code = string.Join(Environment.NewLine, linesToUse).Trim()
.TrimStart("{".ToCharArray()); // Depending on the formatting this may be left over from removing the class
code = Regex.Replace(
code,
@"public\s+?(?'Type'[\w<>]+?)\s(?'Name'\w+?)\s*\((?'Args'[^\)]*?)\)\s*?\{\s*?(throw new NotImplementedException\(\);)",
new MatchEvaluator(
match =>
{
string start = string.Format(
"public {0} {1}({2})\r\n{{",
match.Groups["Type"].Value,
match.Groups["Name"].Value,
match.Groups["Args"].Value);
var args =
match.Groups["Args"].Value.Split(",".ToCharArray())
.Select(s => s.Trim().Split(" ".ToCharArray()))
.ToDictionary(s => s.Last(), s => s.First());
string call = "_decorated." + match.Groups["Name"].Value + "(" + string.Join(",", args.Keys) + ");";
if (match.Groups["Type"].Value != "void")
{
call = "return " + call;
}
string argsStr = args.Keys.Any(s => s.Length > 0) ? ("," + string.Join(",", args.Keys)) : string.Empty;
string loggedCall = string.Format(
"using (BuildLogger(\"{0}\"{1})){{\r\n{2}\r\n}}",
match.Groups["Name"].Value,
argsStr,
call);
return start + "\r\n" + loggedCall;
}));
code = classLine.Trim().TrimEnd("{".ToCharArray()) + "\n{\n" + code + "\n}\n";
यहाँ एक उदाहरण है:
public interface ITestAdapter : IDisposable
{
string TestMethod1();
IEnumerable<string> TestMethod2(int a);
void TestMethod3(List<string[]> a, Object b);
}
फिर LoggingTestAdapter नामक एक क्लास बनाएं, जो ITestAdapter को कार्यान्वित करता है, सभी तरीकों को लागू करने के लिए विज़ुअल स्टूडियो को ऑटो में लाएं और फिर इसे ऊपर दिए गए कोड के माध्यम से चलाएं। आपको तब कुछ इस तरह से होना चाहिए:
public class LoggingTestAdapter : ITestAdapter
{
public void Dispose()
{
using (BuildLogger("Dispose"))
{
_decorated.Dispose();
}
}
public string TestMethod1()
{
using (BuildLogger("TestMethod1"))
{
return _decorated.TestMethod1();
}
}
public IEnumerable<string> TestMethod2(int a)
{
using (BuildLogger("TestMethod2", a))
{
return _decorated.TestMethod2(a);
}
}
public void TestMethod3(List<string[]> a, object b)
{
using (BuildLogger("TestMethod3", a, b))
{
_decorated.TestMethod3(a, b);
}
}
}
यह समर्थन कोड के साथ है:
public class DebugLogger : ILogger
{
private Stopwatch _stopwatch;
public DebugLogger()
{
_stopwatch = new Stopwatch();
_stopwatch.Start();
}
public void Dispose()
{
_stopwatch.Stop();
string argsStr = string.Empty;
if (Args.FirstOrDefault() != null)
{
argsStr = string.Join(",",Args.Select(a => (a ?? (object)"null").ToString()));
}
System.Diagnostics.Debug.WriteLine(string.Format("{0}({1}) @ {2}ms", Name, argsStr, _stopwatch.ElapsedMilliseconds));
}
public string Name { get; set; }
public object[] Args { get; set; }
}
public interface ILogger : IDisposable
{
string Name { get; set; }
object[] Args { get; set; }
}
public class LoggingTestAdapter<TLogger> : ITestAdapter where TLogger : ILogger,new()
{
private readonly ITestAdapter _decorated;
public LoggingTestAdapter(ITestAdapter toDecorate)
{
_decorated = toDecorate;
}
private ILogger BuildLogger(string name, params object[] args)
{
return new TLogger { Name = name, Args = args };
}
public void Dispose()
{
_decorated.Dispose();
}
public string TestMethod1()
{
using (BuildLogger("TestMethod1"))
{
return _decorated.TestMethod1();
}
}
public IEnumerable<string> TestMethod2(int a)
{
using (BuildLogger("TestMethod2", a))
{
return _decorated.TestMethod2(a);
}
}
public void TestMethod3(List<string[]> a, object b)
{
using (BuildLogger("TestMethod3", a, b))
{
_decorated.TestMethod3(a, b);
}
}
}