ฉันประสบปัญหานี้หลายครั้งและฉันคิดว่าฉันได้วิธีง่ายๆ
ตอนแรกฉันไปกับรูปแบบของมัณฑนากรและใช้แต่ละวิธีด้วยตนเองเมื่อคุณมีหลายร้อยวิธีนี้น่าเบื่อมาก
จากนั้นฉันตัดสินใจที่จะใช้ PostSharp แต่ฉันไม่ชอบความคิดที่จะรวมไลบรารีทั้งหมดเพื่อทำสิ่งที่ฉันสามารถทำได้ด้วยโค้ดง่าย ๆ (จำนวนมาก)
จากนั้นฉันไปตามเส้นทางพร็อกซีแบบโปร่งใสซึ่งสนุก แต่เกี่ยวข้องกับการปล่อย IL แบบไดนามิกในเวลาทำงานและจะไม่เป็นสิ่งที่ฉันต้องการทำในสภาพแวดล้อมการผลิต
เมื่อเร็ว ๆ นี้ฉันตัดสินใจที่จะใช้เท็มเพลต T4 เพื่อใช้รูปแบบมัณฑนากรโดยอัตโนมัติในเวลาออกแบบปรากฎว่าเท็มเพลต T4 นั้นใช้งานได้ค่อนข้างยากและฉันต้องการสิ่งนี้อย่างรวดเร็วดังนั้นฉันจึงสร้างรหัสตะโกน มันรวดเร็วและสกปรก (และไม่รองรับคุณสมบัติ) แต่หวังว่าบางคนจะพบว่ามีประโยชน์
นี่คือรหัส:
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 รับ visual studio เพื่อใช้วิธีการทั้งหมดโดยอัตโนมัติแล้วเรียกใช้ผ่านรหัสด้านบน จากนั้นคุณควรมีดังนี้:
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);
}
}
}