我更倾向于在其构造函数中验证类,但有时我会为某些低级类执行此操作以解决性能问题。当我开始制作成千上万个这些简单对象中的10个时,我经常在更高级别的逻辑中验证它们的输入,即使它们也有构造函数验证(逻辑通常在方法中失败 - 在我进入类c&之前#39;职责范围)
我喜欢保留低级验证代码,因为如果我忘记了更高级别的捕获,它有助于调试。我也不想删除低级验证代码以使其更快,因为如果我实现创建这些类的新方法,事情可能会无声地或在意外时间失败。
到目前为止,我忽略了性能问题并始终使用类似的模式验证内部构造函数,如下面的示例代码所示(请参阅Validate.MustBeAbove<T>
扩展方法)。我添加了一个简单而且非常天真的方法来禁用using
语句中的验证,但它显然不是线程安全的,而且它仍然很慢(我假设方法调用正在占用大部分时间??)。
问题:
您应该能够将代码复制粘贴到新的控制台项目中并运行它。
结果
Test Passed
Test Passed
Radius = 123.4
~~~~~~~~~~~~~~~~~
Ranked Results
~~~~~~~~~~~~~~~~~
Method: "Profile_Case_3_UnSafeFooObject_ConstructorAssignment"
Iterations = 100000, Warm Up Calls = 10
Results: Elapsed = 888ms, Average = 0.008875ms
Method: "Profile_Case_4_UnSafeFooObject_PublicFieldAssignment"
Iterations = 100000, Warm Up Calls = 10
Results: Elapsed = 924ms, Average = 0.009242ms
Method: "Profile_Case_2_FooObject_Without_Constructor_Validation"
Iterations = 100000, Warm Up Calls = 10
Results: Elapsed = 1,640ms, Average = 0.016399ms
Method: "Profile_Case_1__FooObject_With_Constructor_Validated"
Iterations = 100000, Warm Up Calls = 10
Results: Elapsed = 1,926ms, Average = 0.019259ms
代码
using FluentValidation;
using Foos;
using Performance;
using ProfileScenarios;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tests;
namespace OptimizeValidation
{
class Program
{
// Constants
const int WARM_UP_CALLS = 10;
const int ITERATIONS = 100000;
const int COUNT = 360;
// Static for Debug
static StringBuilder sb = new StringBuilder();
// Main Entry Point
static void Main(string[] args)
{
double Radius = 123.4;
sb.AppendFormat("Radius = {0}\r\n", Radius);
try
{
// Inline Test Cases
Basic_Inline_Tests.FooObject_created_with_p_radius_of_negative_1_should_throw_ArgumentOutOfRangeException();
Basic_Inline_Tests.FooObject_created_with_p_radius_of_negative_1_in_unchecked_mode_should_not_throw();
}
catch (Exception ex)
{
sb.AppendFormat("Unexpected Exception: {0}\r\n", ex.Message);
}
Profiler
.Benchmark(
p_methodName: "Profile_Case_1__FooObject_With_Constructor_Validated",
p_warmUpCalls: WARM_UP_CALLS,
p_iterations: ITERATIONS,
p_action: () =>
{
List<IFooObject> ListOfFoos = ProfileCases.Profile_Case_1__FooObject_With_Constructor_Validated(Radius, COUNT);
ListOfFoos.Clear();
});
Profiler
.Benchmark(
p_methodName: "Profile_Case_2_FooObject_Without_Constructor_Validation",
p_warmUpCalls: WARM_UP_CALLS,
p_iterations: ITERATIONS,
p_action: () =>
{
List<IFooObject> ListOfFoos = ProfileCases.Profile_Case_2_FooObject_Without_Constructor_Validation(Radius, COUNT);
ListOfFoos.Clear();
});
Profiler
.Benchmark(
p_methodName: "Profile_Case_3_UnSafeFooObject_ConstructorAssignment",
p_warmUpCalls: WARM_UP_CALLS,
p_iterations: ITERATIONS,
p_action: () =>
{
List<IFooObject> ListOfFoos = ProfileCases.Profile_Case_3_UnSafeFooObject_ConstructorAssignment(Radius, COUNT);
ListOfFoos.Clear();
});
Profiler
.Benchmark(
p_methodName: "Profile_Case_4_UnSafeFooObject_PublicFieldAssignment",
p_warmUpCalls: WARM_UP_CALLS,
p_iterations: ITERATIONS,
p_action: () =>
{
List<IFooObject> ListOfFoos = ProfileCases.Profile_Case_4_UnSafeFooObject_PublicFieldAssignment(Radius, COUNT);
ListOfFoos.Clear();
});
sb.AppendFormat("\r\n~~~~~~~~~~~~~~~~~\r\nRanked Results\r\n~~~~~~~~~~~~~~~~~\r\n{0}", Profiler.Ranked_Results(p_descending: false));
Console.WriteLine(sb.ToString());
Console.ReadKey();
}
}
}
namespace FluentValidation
{
public static class Validate
{
// Static Fields
public static bool m_disabled;
/// <summary>
/// Validates the passed in parameter is above a specified limit, throwing a detailed exception message if the test fails.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="p_parameter">Parameter to validate.</param>
/// <param name="p_limit">Limit to test parameter against.</param>
/// <param name="p_name">Name of tested parameter to assist with debugging.</param>
/// <param name="p_variableValueName">Name of limit variable, if limit is not hard-coded.</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static void MustBeAbove<T>(this IComparable<T> p_parameter, T p_limit, string p_name, string p_variableValueName = default(string))
where T : struct
{
// Naive approach
if (m_disabled)
return;
if (p_parameter.CompareTo(p_limit) <= 0)
if (p_variableValueName != default(string))
throw
new
ArgumentOutOfRangeException(string.Format(
"Parameter must be greater than the value of \"{0}\" which was {1}, but \"{2}\" was {3}.",
p_variableValueName, p_limit, p_name, p_parameter), default(Exception));
else
throw
new
ArgumentOutOfRangeException(string.Format(
"Parameter must be greater than {0}, but \"{1}\" was {2}.",
p_limit, p_name, p_parameter), default(Exception));
}
// Some Class to control whether validation actually happens
public class BarContext : IDisposable
{
// What goes here to get the current Thread|Task so that a flag
// can indicate that validation was performed at a higher level
// and doesn't need to be repeated?
public BarContext()
{
// Naive approach
m_disabled = true;
}
// Dispose
public void Dispose()
{
// Naive approach
m_disabled = false;
}
}
// Some method to return a thread specific context object
public static IDisposable UncheckedContext()
{
// What goes here?
return
new BarContext();
}
}
}
namespace ProfileScenarios
{
public static class ProfileCases
{
// Profile Scenarios
public static List<IFooObject> Profile_Case_1__FooObject_With_Constructor_Validated(double p_radius, int p_count)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
p_count
.MustBeAbove(0, "p_count");
var FooList = new List<IFooObject>(p_count);
for (int i = 0; i < p_count; i++)
FooList.Add(new FooObject(p_radius, i));
return
FooList;
}
public static List<IFooObject> Profile_Case_2_FooObject_Without_Constructor_Validation(double p_radius, int p_count)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
p_count
.MustBeAbove(0, "p_count");
var FooList = new List<IFooObject>(p_count);
using (var UncheckedMode = Validate.UncheckedContext())
{
for (int i = 0; i < p_count; i++)
FooList.Add(new FooObject(p_radius, i));
}
return
FooList;
}
public static List<IFooObject> Profile_Case_3_UnSafeFooObject_ConstructorAssignment(double p_radius, int p_count)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
p_count
.MustBeAbove(0, "p_count");
var FooList = new List<IFooObject>(p_count);
for (int i = 0; i < p_count; i++)
FooList.Add(new UnSafeFooObject_ConstructorAssignment(p_radius, i));
return
FooList;
}
public static List<IFooObject> Profile_Case_4_UnSafeFooObject_PublicFieldAssignment(double p_radius, int p_count)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
p_count
.MustBeAbove(0, "p_count");
var FooList = new List<IFooObject>(p_count);
for (int i = 0; i < p_count; i++)
{
var Foo = new UnSafeFooObject_PublicFieldAssignment();
Foo.Radius = p_radius;
Foo.Angle = i;
FooList.Add(Foo);
}
return
FooList;
}
}
}
namespace Tests
{
public static class Basic_Inline_Tests
{
public static void FooObject_created_with_p_radius_of_negative_1_should_throw_ArgumentOutOfRangeException()
{
try
{
// Test
new FooObject(-1, 123);
// Test Failed
throw
new InvalidOperationException(
"FooObject created with p_radius of -1 should throw ArgumentOutOfRangeException.");
}
catch (ArgumentOutOfRangeException)
{
// Test Passed
Console.WriteLine("Test Passed");
}
}
public static void FooObject_created_with_p_radius_of_negative_1_in_unchecked_mode_should_not_throw()
{
try
{
// Test
using (var UncheckedMode = Validate.UncheckedContext())
{
new FooObject(-1, 123);
}
}
catch (ArgumentOutOfRangeException ex)
{
// Test Failed
throw
new InvalidOperationException(
"FooObject created in Unchecked Mode threw an exception.", ex);
}
// Test Passed
Console.WriteLine("Test Passed");
}
}
}
namespace Foos
{
public interface IFooObject
{
/*Placeholder*/
}
public class FooObject : IFooObject
{
// Fields
private double m_radius;
private double m_angle;
// Properties
public double Radius { get { return m_radius; } }
public double Angle { get { return m_angle; } }
// Constructor
public FooObject(double p_radius, double p_angle)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
// Init
m_radius = p_radius;
m_angle = p_angle;
}
}
public class UnSafeFooObject_ConstructorAssignment : IFooObject
{
// Fields
private double m_radius;
private double m_angle;
// Properties
public double Radius { get { return m_radius; } }
public double Angle { get { return m_angle; } }
// Constructor
public UnSafeFooObject_ConstructorAssignment(double p_radius, double p_angle)
{
//// Validate
//p_radius
// .MustBeAboveZero("p_radius");
// Init
m_radius = p_radius;
m_angle = p_angle;
}
}
public class UnSafeFooObject_PublicFieldAssignment : IFooObject
{
// Public Fields
public double Radius;
public double Angle;
}
}
namespace Performance
{
public static class Profiler
{
// Fields
private static List<ResultDetails> m_results = new List<ResultDetails>();
private static readonly object m_syncObject = new object();
// Properties
public static string TimerInfo
{
get
{
return
string.Format("Timer: Frequency = {0:N0}Hz, Resolution = {1:N0}ns\r\n",
Stopwatch.Frequency,
((1000L * 1000L * 1000L) / Stopwatch.Frequency));
}
}
// Methods
public static string Benchmark(string p_methodName, int p_iterations, int p_warmUpCalls, Action p_action)
{
lock (m_syncObject)
{
// Prepare Environment
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
// Warm Up
for (int i = 0; i < p_warmUpCalls; i++)
p_action();
// Profile
var Timer = Stopwatch.StartNew();
for (int i = 0; i < p_iterations; i++)
p_action();
Timer.Stop();
// Return Results
var Details = new StringBuilder();
var Ellapsed = Timer.Elapsed.TotalMilliseconds;
var Average = (Timer.Elapsed.TotalMilliseconds / (double)p_iterations);
Details
.AppendFormat("Method: \"{0}\"\r\n", p_methodName)
.AppendFormat("Iterations = {0:D}, Warm Up Calls = {1:D}\r\n", p_iterations, p_warmUpCalls)
.AppendFormat("Results: Elapsed = {0:N0}ms, Average = {1:N6}ms\r\n", Ellapsed, Average);
m_results.Add(new ResultDetails(Average, Details.ToString()));
return Details.ToString();
}
}
public static string Ranked_Results(bool p_descending)
{
lock (m_syncObject)
{
var sb = new StringBuilder();
var OrderedList = (p_descending) ? m_results.OrderByDescending(result => result.Average) : m_results.OrderBy(result => result.Average);
foreach (var Result in OrderedList)
sb.AppendLine(Result.Details);
return sb.ToString();
}
}
public static void Clear()
{
lock (m_syncObject)
{
m_results.Clear();
}
}
// Child Class
private class ResultDetails
{
public double Average { get; private set; }
public string Details { get; private set; }
public ResultDetails(double p_average, string p_details)
{
Average = p_average;
Details = p_details;
}
}
}
}
答案 0 :(得分:1)
它有点难看,但在昨天编写了探查器案例后,我意识到方法调用可能是验证代码中最重要的部分(请参阅&#34;天真&#34;方法2中的案例2)原帖...它对性能的影响非常小。)
这让我想到了#34;理想&#34;没有任何验证代码只会像普通的构造函数或属性赋值一样快......但我鄙视编译器指令禁用代码(我不惜一切代价避免使用它们)而且我还没有想过向下一个程序员指示重载意图的好方法(因为重载本身不具有唯一的名称)。
所以我选择了一个抛弃类,它只用于表示应该调用未经验证的构造函数而不是验证的构造函数。它非常非常接近理想的性能水平(请注意,我的OP结果处于调试模式,附带调试器,因为我忘了将其设置为发布。下面的结果来自没有附带调试器的发布版本,与OP相比,所有方法都更快。
我原来问题的答案是:
如何以避免线程锁定的方式实现BarContext,但是指示从c&#tor;到Dispose(),应该跳过Validations?
答:您可以将ThreadStatic变量设置为标志,但它需要在类之间进行协作以获得最佳性能。此外,在生成Task
的情况下,该标志将不会继续,并且需要单独为每个子任务设置。
Profile_Case_2如何更接近Profile_Case_3的性能?
答:你必须首先避免调用,它似乎是验证代码中最重的部分。也就是说,即使是对ThreadStatic变量的评估也会导致与理想情况相比性能下降约30%,这表明这已经非常快速地发挥作用。
我应该做什么&#34;不安全&#34;这些低级别类型的构造函数?
答:是的,重载类构造函数以允许未经验证的参数赋值。您不会获得比基于测试更好的性能。使用具有明确名称的虚拟类来指示其强制过载的目的也有助于其他程序员理解代码的意图。
WARM_UP_CALLS = 10
ITERATIONS = 100000
COUNT = 360
Radius = 123.4
Test Passed: p_radius of -1 was invalid.
Test Passed: p_radius of -1 was ignored using unchecked mode.
Test Passed: p_radius of -1 was ignored by unsafe overload.
Test Passed: p_radius of -1 after dispose was invalid.
~~~~~~~~~~~~~~~~~
Ranked Results
~~~~~~~~~~~~~~~~~
Method: "Profile_Case_4_UnSafeFooObject_PublicFieldAssignment"
Iterations = 100000, Warm Up Calls = 10
Results: Elapsed = 666ms, Average = 0.006658ms, Efficiency = 100.00%
Method: "Profile_Case_5_FooObject_With_UnSafeOverload"
Iterations = 100000, Warm Up Calls = 10
Results: Elapsed = 670ms, Average = 0.006695ms, Efficiency = 99.44%
Method: "Profile_Case_3_UnSafeFooObject_ConstructorAssignment"
Iterations = 100000, Warm Up Calls = 10
Results: Elapsed = 674ms, Average = 0.006737ms, Efficiency = 98.83%
Method: "Profile_Case_2_FooObject_using_UnSafeFlag"
Iterations = 100000, Warm Up Calls = 10
Results: Elapsed = 898ms, Average = 0.008975ms, Efficiency = 74.18%
Method: "Profile_Case_1_FooObject_With_Constructor_Validation"
Iterations = 100000, Warm Up Calls = 10
Results: Elapsed = 1,081ms, Average = 0.010812ms, Efficiency = 61.58%
Press Escape to exit, or any other key to run again.
namespace FluentValidation
{
public class ValidateOverload
{ }
public static class Validate
{
// Static Fields
public static ValidateOverload UnSafeOverload = new ValidateOverload();
...
}
}
它略微打破了DRY原则,但目的很明确(我可以将验证版本称为: this()
c,但由于调用,代码很难理解构造函数的顺序...所以我宁愿打破像这样的简单类的DRY原则。)
// Constructor
public FooObject(double p_radius, double p_angle)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
// Init
m_radius = p_radius;
m_angle = p_angle;
}
public FooObject(double p_radius, double p_angle, ValidateOverload p_uncheckedMode)
{
// Init
m_radius = p_radius;
m_angle = p_angle;
}
来自OP的更新代码,请参阅Profile_Case_5
using FluentValidation;
using Foos;
using Performance;
using ProfileScenarios;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tests;
namespace OptimizeValidation
{
class Program
{
// Constants
const int WARM_UP_CALLS = 10;
const int ITERATIONS = 100000;
const int COUNT = 360;
// Static for Debug
static StringBuilder sb = new StringBuilder();
// Main Entry Point
static void Main(string[] args)
{
double Radius = 123.4;
Console.WriteLine("WARM_UP_CALLS = {0}", WARM_UP_CALLS);
Console.WriteLine("ITERATIONS = {0}", ITERATIONS);
Console.WriteLine("COUNT = {0}", COUNT);
Console.WriteLine("Radius = {0}", Radius);
try
{
// Inline Test Cases
Basic_Inline_Tests.FooObject_created_with_p_radius_of_negative_1_should_throw_ArgumentOutOfRangeException();
Basic_Inline_Tests.FooObject_created_with_p_radius_of_negative_1_using_unchecked_mode_should_not_throw();
Basic_Inline_Tests.FooObject_created_with_p_radius_of_negative_1_with_unsafe_overload_should_not_throw();
Basic_Inline_Tests.FooObject_created_with_p_radius_of_negative_1_using_unchecked_mode_should_throw_after_dispose();
}
catch (Exception ex)
{
sb.AppendFormat("Unexpected Exception: {0}\r\n", ex.Message);
}
do
{
Profiler
.Benchmark(
p_methodName: "Profile_Case_1_FooObject_With_Constructor_Validation",
p_warmUpCalls: WARM_UP_CALLS,
p_iterations: ITERATIONS,
p_action: () =>
{
List<IFooObject> ListOfFoos = ProfileCases.Profile_Case_1_FooObject_With_Constructor_Validation(Radius, COUNT);
ListOfFoos.Clear();
});
Profiler
.Benchmark(
p_methodName: "Profile_Case_2_FooObject_using_UnSafeFlag",
p_warmUpCalls: WARM_UP_CALLS,
p_iterations: ITERATIONS,
p_action: () =>
{
List<IFooObject> ListOfFoos = ProfileCases.Profile_Case_2_FooObject_using_UnSafeFlag(Radius, COUNT);
ListOfFoos.Clear();
});
Profiler
.Benchmark(
p_methodName: "Profile_Case_3_UnSafeFooObject_ConstructorAssignment",
p_warmUpCalls: WARM_UP_CALLS,
p_iterations: ITERATIONS,
p_action: () =>
{
List<IFooObject> ListOfFoos = ProfileCases.Profile_Case_3_UnSafeFooObject_ConstructorAssignment(Radius, COUNT);
ListOfFoos.Clear();
});
Profiler
.Benchmark(
p_methodName: "Profile_Case_4_UnSafeFooObject_PublicFieldAssignment",
p_warmUpCalls: WARM_UP_CALLS,
p_iterations: ITERATIONS,
p_action: () =>
{
List<IFooObject> ListOfFoos = ProfileCases.Profile_Case_4_UnSafeFooObject_PublicFieldAssignment(Radius, COUNT);
ListOfFoos.Clear();
});
Profiler
.Benchmark(
p_methodName: "Profile_Case_5_FooObject_With_UnSafeOverload",
p_warmUpCalls: WARM_UP_CALLS,
p_iterations: ITERATIONS,
p_action: () =>
{
List<IFooObject> ListOfFoos = ProfileCases.Profile_Case_5_FooObject_With_UnSafeOverload(Radius, COUNT);
ListOfFoos.Clear();
});
sb
.AppendFormat(
"\r\n~~~~~~~~~~~~~~~~~\r\nRanked Results\r\n~~~~~~~~~~~~~~~~~\r\n{0}",
Profiler.Ranked_Results(p_descending: false));
Console.WriteLine(sb.ToString());
Console.WriteLine("Press Escape to exit, or any other key to run again.");
// Reset
Profiler.Clear();
sb.Clear();
}
while (Console.ReadKey().Key != ConsoleKey.Escape);
}
}
}
namespace FluentValidation
{
public class ValidateOverload
{
}
public static class Validate
{
// ThreadStatic see example code http://msdn.microsoft.com/en-us/library/system.threading.thread.setdata%28v=vs.110%29.aspx
// Static Fields
[ThreadStatic]
public static bool UncheckedMode;
public static ValidateOverload UnSafeOverload = new ValidateOverload();
/// <summary>
/// Validates the passed in parameter is above a specified limit, throwing a detailed exception message if the test fails.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="p_parameter">Parameter to validate.</param>
/// <param name="p_limit">Limit to test parameter against.</param>
/// <param name="p_name">Name of tested parameter to assist with debugging.</param>
/// <param name="p_variableValueName">Name of limit variable, if limit is not hard-coded.</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static void MustBeAbove<T>(this IComparable<T> p_parameter, T p_limit, string p_name, string p_variableValueName = default(string))
where T : struct
{
if (p_parameter.CompareTo(p_limit) <= 0)
if (p_variableValueName != default(string))
throw
new
ArgumentOutOfRangeException(string.Format(
"Parameter must be greater than the value of \"{0}\" which was {1}, but \"{2}\" was {3}.",
p_variableValueName, p_limit, p_name, p_parameter), default(Exception));
else
throw
new
ArgumentOutOfRangeException(string.Format(
"Parameter must be greater than {0}, but \"{1}\" was {2}.",
p_limit, p_name, p_parameter), default(Exception));
}
// Method
internal static IDisposable UncheckedThreadContext()
{
return
new UnCheckedContext();
}
private class UnCheckedContext : IDisposable
{
public UnCheckedContext()
{
UncheckedMode = true;
}
// Dispose
public void Dispose()
{
UncheckedMode = false;
}
}
}
}
namespace ProfileScenarios
{
public static class ProfileCases
{
// Profile Scenarios
public static List<IFooObject> Profile_Case_1_FooObject_With_Constructor_Validation(double p_radius, int p_count)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
p_count
.MustBeAbove(0, "p_count");
var FooList = new List<IFooObject>(p_count);
for (int i = 0; i < p_count; i++)
FooList.Add(new FooObject(p_radius, i));
return
FooList;
}
public static List<IFooObject> Profile_Case_2_FooObject_using_UnSafeFlag(double p_radius, int p_count)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
p_count
.MustBeAbove(0, "p_count");
var FooList = new List<IFooObject>(p_count);
using (Validate.UncheckedThreadContext())
{
for (int i = 0; i < p_count; i++)
FooList.Add(new FooObject_IfUnChecked(p_radius, i));
}
return
FooList;
}
public static List<IFooObject> Profile_Case_3_UnSafeFooObject_ConstructorAssignment(double p_radius, int p_count)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
p_count
.MustBeAbove(0, "p_count");
var FooList = new List<IFooObject>(p_count);
for (int i = 0; i < p_count; i++)
FooList.Add(new UnSafeFooObject_ConstructorAssignment(p_radius, i));
return
FooList;
}
public static List<IFooObject> Profile_Case_4_UnSafeFooObject_PublicFieldAssignment(double p_radius, int p_count)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
p_count
.MustBeAbove(0, "p_count");
var FooList = new List<IFooObject>(p_count);
for (int i = 0; i < p_count; i++)
{
var Foo = new UnSafeFooObject_PublicFieldAssignment();
Foo.Radius = p_radius;
Foo.Angle = i;
FooList.Add(Foo);
}
return
FooList;
}
public static List<IFooObject> Profile_Case_5_FooObject_With_UnSafeOverload(double p_radius, int p_count)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
p_count
.MustBeAbove(0, "p_count");
var FooList = new List<IFooObject>(p_count);
for (int i = 0; i < p_count; i++)
FooList.Add(new FooObject(p_radius, i, Validate.UnSafeOverload));
return
FooList;
}
}
}
namespace Tests
{
public static class Basic_Inline_Tests
{
public static void FooObject_created_with_p_radius_of_negative_1_should_throw_ArgumentOutOfRangeException()
{
try
{
// Test
new FooObject(-1, 123);
// Test Failed
throw
new InvalidOperationException(
"FooObject created with p_radius of -1 should throw ArgumentOutOfRangeException.");
}
catch (ArgumentOutOfRangeException)
{
// Test Passed
Console.WriteLine("Test Passed: p_radius of -1 was invalid.");
}
}
public static void FooObject_created_with_p_radius_of_negative_1_using_unchecked_mode_should_not_throw()
{
try
{
// Test
using (Validate.UncheckedThreadContext())
{
new FooObject_IfUnChecked(-1, 123);
}
}
catch (ArgumentOutOfRangeException ex)
{
// Test Failed
throw
new InvalidOperationException(
"FooObject created using unchecked mode threw an unexpected exception.", ex);
}
// Test Passed
Console.WriteLine("Test Passed: p_radius of -1 was ignored using unchecked mode.");
}
public static void FooObject_created_with_p_radius_of_negative_1_using_unchecked_mode_should_throw_after_dispose()
{
try
{
// Test
using (Validate.UncheckedThreadContext())
{
new FooObject_IfUnChecked(-1, 123);
}
new FooObject_IfUnChecked(-1, 123);
// Test Failed
throw
new InvalidOperationException(
"FooObject created with p_radius of -1 after dispose should throw ArgumentOutOfRangeException.");
}
catch (ArgumentOutOfRangeException)
{
// Test Passed
Console.WriteLine("Test Passed: p_radius of -1 after dispose was invalid.");
}
}
public static void FooObject_created_with_p_radius_of_negative_1_with_unsafe_overload_should_not_throw()
{
try
{
// Test
new FooObject(-1, 123, Validate.UnSafeOverload);
}
catch (ArgumentOutOfRangeException ex)
{
// Test Failed
throw
new InvalidOperationException(
"FooObject created with unsafe overload threw an unexpected exception.", ex);
}
// Test Passed
Console.WriteLine("Test Passed: p_radius of -1 was ignored by unsafe overload.");
}
}
}
namespace Foos
{
public interface IFooObject
{
/*Placeholder*/
}
public class FooObject : IFooObject
{
// Fields
private double m_radius;
private double m_angle;
// Properties
public double Radius { get { return m_radius; } }
public double Angle { get { return m_angle; } }
// Constructor
public FooObject(double p_radius, double p_angle)
{
// Validate
p_radius
.MustBeAbove(0, "p_radius");
// Init
m_radius = p_radius;
m_angle = p_angle;
}
public FooObject(double p_radius, double p_angle, ValidateOverload p_uncheckedMode)
{
// Init
m_radius = p_radius;
m_angle = p_angle;
}
}
public class FooObject_IfUnChecked : IFooObject
{
// Fields
private double m_radius;
private double m_angle;
// Properties
public double Radius { get { return m_radius; } }
public double Angle { get { return m_angle; } }
// Constructor
public FooObject_IfUnChecked(double p_radius, double p_angle)
{
// Validate
if (!Validate.UncheckedMode)
{
p_radius
.MustBeAbove(0, "p_radius");
}
// Init
m_radius = p_radius;
m_angle = p_angle;
}
}
public class UnSafeFooObject_ConstructorAssignment : IFooObject
{
// Fields
private double m_radius;
private double m_angle;
// Properties
public double Radius { get { return m_radius; } }
public double Angle { get { return m_angle; } }
// Constructor
public UnSafeFooObject_ConstructorAssignment(double p_radius, double p_angle)
{
// Init
m_radius = p_radius;
m_angle = p_angle;
}
}
public class UnSafeFooObject_PublicFieldAssignment : IFooObject
{
// Public Fields
public double Radius;
public double Angle;
}
}
namespace Performance
{
public static class Profiler
{
// Fields
private static List<ResultDetails> m_results = new List<ResultDetails>();
private static readonly object m_syncObject = new object();
// Properties
public static string TimerInfo
{
get
{
return
string.Format("Timer: Frequency = {0:N0}Hz, Resolution = {1:N0}ns\r\n",
Stopwatch.Frequency,
((1000L * 1000L * 1000L) / Stopwatch.Frequency));
}
}
// Methods
public static string Benchmark(string p_methodName, int p_iterations, int p_warmUpCalls, Action p_action)
{
// Profiler example http://stackoverflow.com/a/1048708/1718702
lock (m_syncObject)
{
// Prepare Environment
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
// Warm Up
for (int i = 0; i < p_warmUpCalls; i++)
p_action();
// Profile
var MethodTimer = Stopwatch.StartNew();
for (int i = 0; i < p_iterations; i++)
p_action();
MethodTimer.Stop();
// Return Results
var Details = new StringBuilder();
var Ellapsed = MethodTimer.Elapsed.TotalMilliseconds;
var Average = (MethodTimer.Elapsed.TotalMilliseconds / (double)p_iterations);
Details
.AppendFormat("Method: \"{0}\"\r\n", p_methodName)
.AppendFormat("Iterations = {0:D}, Warm Up Calls = {1:D}\r\n", p_iterations, p_warmUpCalls)
.AppendFormat("Results: Elapsed = {0:N0}ms, Average = {1:N6}ms\r\n", Ellapsed, Average);
m_results.Add(new ResultDetails(Average, Details.ToString()));
return Details.ToString();
}
}
public static string Ranked_Results(bool p_descending)
{
lock (m_syncObject)
{
var sb = new StringBuilder();
var OrderedList = (p_descending) ? m_results.OrderByDescending(result => result.Average) : m_results.OrderBy(result => result.Average);
double FastestImplementation = OrderedList.Select(r => r.Average).Min();
foreach (var Result in OrderedList)
{
sb
.Append(Result.Details.TrimEnd())
.AppendFormat(", Efficiency = {0:N2}%", (FastestImplementation / Result.Average) * 100)
.AppendLine()
.AppendLine();
}
return sb.ToString();
}
}
public static void Clear()
{
lock (m_syncObject)
{
m_results.Clear();
}
}
// Child Class
private class ResultDetails
{
public double Average { get; private set; }
public string Details { get; private set; }
public ResultDetails(double p_average, string p_details)
{
Average = p_average;
Details = p_details;
}
}
}
}
答案 1 :(得分:0)
您是否只想创建用于有条件地编译验证代码的编译器常量(“DEBUGCHECKED”)?您的发布版本(经过全面测试)可以省略该指令并获得性能优势,但在开发中(您希望会出现错误),您可以使用它来选择验证代码。