Click here to Skip to main content
15,896,413 members
Articles / Programming Languages / C#

Some Useful Concurrency Classes and A Small Testbench

Rate me:
Please Sign up or sign in to vote.
4.92/5 (37 votes)
15 Jan 2007CPOL70 min read 104.2K   1K   140  
Useful concurrency classes and small test bench in C#
using System;
using System.Threading;
using System.Reflection;
//For TypeDescriptors.
//using System.ComponentModel;
using System.Collections.Generic;
using NUnit.Framework;

using MPFramework.AppCore.QualityAssurance;
using MPFramework.AppCore.PlatformUtilities;
using MPFramework.AppCore.Manufacturing.TypeHandling;

namespace MPFramework.AppCore.Manufacturing.SpecializedTypes
{
	#region Static Test Data Class
	/// <summary>
	/// Container for common test data.
	/// </summary>
	public static class QATester_ReferencedValueType_Testdata
	{
		#region Class Fields
		#endregion // Class Fields
		#region Static Constructor
		static QATester_ReferencedValueType_Testdata()
		{
		}
		#endregion // Static Constructor
	}
	#endregion // Static Test Data Class
	#region Tests
	/// <summary>
	/// Level 1 tests for functions in ReferencedValueType.
	/// </summary>
	[TestFixture]
	[Category(QAUtils.TestLevel1Name)]
	public class QATester_ReferencedValueType_1
	{
		#region Class Fields
		//////// non-generic RVTs instantiated with Int32s.
		// First RVT just wraps an Int32. Value = 0.
		ReferencedValueType m_rVT1 = new ReferencedValueType((System.ValueType)(Int32)0);
		// Second RVT just wraps an Int32. Value = 0.
		ReferencedValueType m_rVT2 = new ReferencedValueType((System.ValueType)(Int32)0);
		// Third RVT just wraps an Int32. Value = 1. Set in constructor.
		ReferencedValueType m_rVT3 = new ReferencedValueType((System.ValueType)(Int32)1);
		//////// non-generic RVT's wrapping a simple struct. 
		// First RVT just wraps the struct. Value = 0,0. We call the parameterized
		// constructor on RVTReferencedSSType. We could handle the Type this way, but we
		// choose to handle it as a ReferencedValueType, so we cast it to m_rVTSS1
		// at construction time in QATester_ReferencedValueType_1(). There are
		// situations in which we might wish to handle such a Type either way.
		RVTReferencedSSType m_rVTSS1_temp
			= new RVTReferencedSSType(new CSharpSimpleStruct());
		ReferencedValueType m_rVTSS1;
		// Second RVT just wraps the same simple struct. Value = 0,0. This one is used to
		// show how to close an open generic RVT at the point of instantiation.
		ReferencedValueType m_rVTSS2 = new ReferencedValueType((System.ValueType)(new CSharpSimpleStruct()));
		// Third RVT just wraps the same simple struct. Value = 1,0.
		ReferencedValueType m_rVTSS3 = new ReferencedValueType((System.ValueType)(new CSharpSimpleStruct(1, 0)));
		// Fourth RVT just wraps the same simple struct. Value = 0,1.
		ReferencedValueType m_rVTSS4 = new ReferencedValueType((System.ValueType)(new CSharpSimpleStruct(0, 1)));
		//////// Int32 RVT's with a reflection access method.
		// First RVT just wraps an Int32. Value = 0.
		TestReflectionAccessInt32RVT m_rVTRInt32TRA1 = new TestReflectionAccessInt32RVT();
		// Second RVT just wraps an Int32. Value = 0.
		TestReflectionAccessInt32RVT m_rVTRInt32TRA2 = new TestReflectionAccessInt32RVT();
		// Third RVT just wraps an Int32. Value = 1. Set in QATester_ReferencedValueType_1().
		TestReflectionAccessInt32RVT m_rVTRInt32TRA3 = new TestReflectionAccessInt32RVT();
		//////// Arbitrary Struct Generic RVT's.
		// First RVT doesn't implement equatable. Value = 0,0.
		GenericRVTReferencedStructClass m_rVTRST1 = new GenericRVTReferencedStructClass();
		// Second one doesn't implement equatable, either. Value = 0,0.
		GenericRVTReferencedStructClass m_rVTRST2 = new GenericRVTReferencedStructClass();
		//////// The rest (from here down) have to be set in the constructor (not default values).
		// Third one doesn't implement equatable, either. Value = 1,1.
		GenericRVTReferencedStructClass m_rVTRST3 = new GenericRVTReferencedStructClass();
		// Next two implement equatable with different numbers (2,2), (3,3).
		GenericRVTEquatableReferencedStructClass m_rVTERST1 = new GenericRVTEquatableReferencedStructClass();
		GenericRVTEquatableReferencedStructClass m_rVTERST2 = new GenericRVTEquatableReferencedStructClass();
		//////// RVT's instantiated as IValueProp's closed as an Int32 (an Int32RVTVP).
		// Usual deal - two the same, one different.
		ReferencedValueType<Int32> m_Int32RVTVP1 = new Int32RVTVP(0);
		ReferencedValueType<Int32> m_Int32RVTVP2 = new Int32RVTVP(0);
		ReferencedValueType<Int32> m_Int32RVTVP3 = new Int32RVTVP(1);
		#endregion // Class Fields
		/// <summary>
		/// Obligatory default NUnit constructor. This one actually does something :-)
		/// </summary>
		public QATester_ReferencedValueType_1()
		{
			//// Set up Int32 RVT's
			m_rVTRInt32TRA3.Value = 1;
			//// Set up the struct RVT's.
			// Create temp structs, load them and copy.
			CSharpSimpleStruct tempRVTS = new CSharpSimpleStruct();
			tempRVTS.FirstInt32 = 1;
			tempRVTS.SecondInt32 = 1;
			m_rVTRST3.Value = tempRVTS;
			RVTEquatableStruct tempRVTES = new RVTEquatableStruct();
			tempRVTES.FirstInt32 = 2;
			tempRVTES.SecondInt32 = 2;
			m_rVTERST1.Value = tempRVTES;
			tempRVTES.FirstInt32 = 3;
			tempRVTES.SecondInt32 = 3;
			m_rVTERST2.Value = tempRVTES;

			// For our wrapped struct demo.
			m_rVTSS1 = (ReferencedValueType)m_rVTSS1_temp;
		}
		/// <summary>
		/// Placeholder.
		/// </summary>
		[TestFixtureSetUp]
		public void TestFixtureSetup(){	}
		/// <summary>
		/// Placeholder.
		/// </summary>
		[TestFixtureTearDown]
		public void TestFixtureTearDown(){ }
		#region Quantitative Tests
		///////////////////////////////////////////////////////////////////////
		// This section contains tests that verify numerical accuracy of results
		// through NUnit assertions. These tests are generally designed to verify
		// that results of manipulations of RVT's turn out the way they are
		// supposed to.
		///////////////////////////////////////////////////////////////////////
		/// <summary>
		/// This test checks our equality and swap methods on wrapped ints to
		/// see if they give the right answer.
		/// </summary>
		[Test(Description = "Level 1 series a tests for ReferencedValueType")]
		public void QATester_RunTest_ReferencedValueType_a()
		{
			// For all of the equality tests.
			bool areEqual = false;
			////////// Check out IsValueSameAs first because we'll use it.
			//// generic IsValueSameAs tests for IReferencedValueType<T> closed as Int32 on
			//// an RVT instantiated as Int32.
			areEqual = ((IReferencedValueType<Int32>)m_rVTRInt32TRA1).IsValueSameAs(m_rVTRInt32TRA3);
			Assert.IsFalse(areEqual, "generic IsValueSameAs is broken (are equal)");
			areEqual = ((IReferencedValueType<Int32>)m_rVTRInt32TRA1).IsValueSameAs(m_rVTRInt32TRA2);
			Assert.IsTrue(areEqual, "generic IsValueSameAs is broken (are not equal)");
			//// non-generic IsValueSameAs tests for IReferencedValueType RVT instantiated as Int32.
			areEqual = ((IReferencedValueType)m_rVT1).IsValueSameAs(m_rVT3);
			Assert.IsFalse(areEqual, "non-generic IsValueSameAs is broken (are equal)");
			areEqual = ((IReferencedValueType)m_rVT1).IsValueSameAs(m_rVT2);
			Assert.IsTrue(areEqual, "non-generic IsValueSameAs is broken (are not equal)");
			//// non-generic IsValueSameAs tests for IReferencedValueType RVT instantiated as SimpleStruct.
			areEqual = ((IReferencedValueType)m_rVTSS1).IsValueSameAs(m_rVTSS3);
			Assert.IsFalse(areEqual, "non-generic SimpleStruct IsValueSameAs is broken (are equal)");
			areEqual = ((IReferencedValueType)m_rVTSS1).IsValueSameAs(m_rVTSS2);
			Assert.IsTrue(areEqual, "non-generic SimpleStruct IsValueSameAs is broken (are not equal)");
			// This one will compare equal because of SimpleStruct's special treatment
			// in RVT.Equals(ValueType, ValueType).
			areEqual = ((IReferencedValueType)m_rVTSS1).IsValueSameAs(m_rVTSS4);
			Assert.IsTrue(areEqual, "non-generic SimpleStruct IsValueSameAs is broken (are not equal)");
			//// generic IsValueSameAs tests for IReferencedValueType<T> closed as Int32 on
			//// an RVT instantiated as Int32RVTVP.
			areEqual = ((IReferencedValueType<Int32>)m_Int32RVTVP1).IsValueSameAs(m_Int32RVTVP3);
			Assert.IsFalse(areEqual, "generic RVTVP.IsValueSameAs is broken (are equal)");
			areEqual = ((IReferencedValueType<Int32>)m_Int32RVTVP1).IsValueSameAs(m_Int32RVTVP2);
			Assert.IsTrue(areEqual, "generic RVTVP.IsValueSameAs is broken (are not equal)");

			////////// Swapping tests.
			//// generic swapping tests for IReferencedValueType<T> closed as Int32 on
			//// an RVT instantiated as Int32.
			((IReferencedValueType<Int32>)m_rVTRInt32TRA1).SwapValues(m_rVTRInt32TRA3);
			areEqual = ((IEquatable<IReferencedValueType>)m_rVTRInt32TRA1).Equals(m_rVTRInt32TRA2);
			Assert.IsFalse(areEqual, "generic swap is broken (are equal)");
			// Put them back and check again.
			((IReferencedValueType<Int32>)m_rVTRInt32TRA1).SwapValues(m_rVTRInt32TRA3);
			areEqual = ((IEquatable<IReferencedValueType>)m_rVTRInt32TRA1).Equals(m_rVTRInt32TRA2);
			Assert.IsTrue(areEqual, "generic swap is broken(are not equal)");
			//// non-generic swapping tests for IReferencedValueType on an RVT instantiated as Int32.
			((IReferencedValueType)m_rVTRInt32TRA1).SwapValues(m_rVTRInt32TRA3);
			areEqual = ((IEquatable<IReferencedValueType>)m_rVTRInt32TRA1).Equals(m_rVTRInt32TRA2);
			Assert.IsFalse(areEqual, "non-generic swap is broken (are equal)");
			// Swap back just to make sure.
			((IReferencedValueType)m_rVTRInt32TRA3).SwapValues(m_rVTRInt32TRA1);
			areEqual = ((IEquatable<IReferencedValueType>)m_rVTRInt32TRA1).Equals(m_rVTRInt32TRA2);
			Assert.IsTrue(areEqual, "non-generic swap is broken (are not equal)");
			//// generic swapping tests for IReferencedValueType<T> closed as Int32 on
			//// an RVT instantiated as Int32RVTVP.
			((IReferencedValueType<Int32>)m_Int32RVTVP1).SwapValues(m_Int32RVTVP3);
			areEqual = ((IEquatable<IReferencedValueType>)m_Int32RVTVP1).Equals(m_Int32RVTVP2);
			Assert.IsFalse(areEqual, "generic RVTVP.SwapValues is broken (are equal)");
			((IReferencedValueType<Int32>)m_Int32RVTVP3).SwapValues(m_Int32RVTVP1);
			areEqual = ((IEquatable<IReferencedValueType>)m_Int32RVTVP1).Equals(m_Int32RVTVP2);
			Assert.IsTrue(areEqual, "generic RVTVP.SwapValues is broken (are not equal)");

			//// IEquatable tests.
			// Two wrapped Int32's with same value should compare identical.
			areEqual = ((IEquatable<IReferencedValueType>)m_rVTRInt32TRA1).Equals(m_rVTRInt32TRA2);
			Assert.IsTrue(areEqual, "Int32 comparison is broken (0!=0)");
			// Two wrapped Int32's with different value should compare nonidentical.
			areEqual = ((IEquatable<IReferencedValueType>)m_rVTRInt32TRA1).Equals(m_rVTRInt32TRA3);
			Assert.IsFalse(areEqual, "Int32 comparison is broken(0==1)");
		}
		/// <summary>
		/// This test checks our equality methods on wrapped structs to see if they give
		/// the right answer.
		/// </summary>
		[Test(Description = "Level 1 series b tests for ReferencedValueType")]
		public void QATester_RunTest_ReferencedValueType_b()
		{
			// This test is designed to verify RVT.Equals(ValueType). We take one RVT's
			// internal BoxedValue and compare it with another RVT. We use our simple
			// non-Generic RVT's m_rVT1, m_rVT2 and m_rVT3. 
			bool areEqual = ((IEquatable<System.ValueType>)m_rVT1).Equals(m_rVT2.BoxedValue);
			Assert.IsTrue(areEqual, "Equals(ValueType) is broken ((0,0)!=(0,0))");
			// Same deal when they are different.
			areEqual = ((IEquatable<System.ValueType>)m_rVT1).Equals(m_rVT3.BoxedValue);
			Assert.IsFalse(areEqual, "Equals(ValueType) is broken ((0,0)==(1,1))");

			// Try again with our OPs.
			areEqual = (m_rVT1 == m_rVT2);
			Assert.IsTrue(areEqual, "ReferencedValueType == ReferencedValueType is broken ((0,0)!=(0,0))");
			areEqual = !(m_rVT1 != m_rVT3);
			Assert.IsFalse(areEqual, "ReferencedValueType != ReferencedValueType is broken ((0,0)==(1,1))");

			// One more time with our lopsided OPs.
			areEqual = (m_rVT1 == ((IReferencedValueType)m_rVT2));
			Assert.IsTrue(areEqual, "ReferencedValueType == IReferencedValueType is broken ((0,0)!=(0,0))");
			areEqual = !(m_rVT1 != ((IReferencedValueType)m_rVT3));
			Assert.IsFalse(areEqual, "ReferencedValueType != IReferencedValueType is broken ((0,0)==(1,1))");

			// This test is pretty simple. Two wrapped structs of the same value
			// should compare equal with our equals method even though the internal
			// structs don't implement IEquatable. This will call our IEquatable.Equals()
			// on the RVT's. Object.Equals() on the structs is supposed to be done with
			// reflection to check all the fields. We'll see.......
			areEqual = ((IEquatable<IReferencedValueType>)m_rVTRST1).Equals(m_rVTRST2);
			Assert.IsTrue(areEqual, "Object.Equals applied to struct's is broken ((0,0)!=(0,0))");
			// Well, wadaya' know - it works..... KRM - only tested in MS.......
			// Same deal when they are different.
			areEqual = ((IEquatable<IReferencedValueType>)m_rVTRST1).Equals(m_rVTRST3);
			Assert.IsFalse(areEqual, "Object.Equals applied to struct's is broken ((0,0)==(1,1))");
			//// Now test the structs that implement IEquatable.
			// First with equatable structs that are the same.
			areEqual = ((IEquatable<IReferencedValueType>)m_rVTRST1).Equals(m_rVTRST2);
			Assert.IsTrue(areEqual, "RVTEquatableStruct's IEquatable.Equals is broken ((0,0)!=(0,0))");
			// Now try equatable structs that are different.
			areEqual = ((IEquatable<IReferencedValueType>)m_rVTRST1).Equals(m_rVTRST3);
			Assert.IsFalse(areEqual, "RVTEquatableStruct's IEquatable.Equals is broken ((0,0)==(1,1))");
		}
		/// <summary>
		/// This test checks Interlocked OP operation with our RVT's.
		/// </summary>
		[Test(Description = "Level 1 series c tests for ReferencedValueType")]
		public void QATester_RunTest_ReferencedValueType_c()
		{
			//// We want to see what Interlocked.Compare(T,T,T) does with our RVT's.
			// nonEquatable structs first.
			m_rVTRST1 = Interlocked.CompareExchange<GenericRVTReferencedStructClass>(ref m_rVTRST1, m_rVTRST2, m_rVTRST3);
			// We already know that Our IEquatable on the RVT works from the last experiment,
			// so let's use it.
			bool areEqual = ((IEquatable<IReferencedValueType>)m_rVTRST1).Equals(m_rVTRST3);
			// Unfortunately, following assertion fails. Apparently MS is doing reference
			// equality like in the System.Object version and doesn't bother to look for
			// IEquatable<T>.
			//			Assert.IsTrue(areEqual, "CompareExchange not exchanging on equal nonEquatables");
			// We'll put this in to let us know if it ever gets working.
			Assert.IsFalse(areEqual, "CompareExchanging is exchanging on equal nonEquatables (it's working now!!!!)");
			// Put m_rVTRST1 back - just in case it ever works.
			m_rVTRST1 = new GenericRVTReferencedStructClass();
			// This one should fail.
			//m_rVTRST1 = Interlocked.CompareExchange<RVTReferencedStructType>(ref m_rVTRST1, m_rVTRST3, m_rVTRST3);
			//areEqual = ((IEquatable<IReferencedValueType>)m_rVTRST1).Equals(m_rVTRST3);
			//Assert.AreEqual(areEqual, "CompareExchanging exchanging nonequal nonEquatables");

			//// Let's try this again on  Hopefully
			// it looks for IEquatable..... The exchange should take place if it does.
		}
		/// <summary>
		/// This test demonstrates miscellaneous ways to access RVT's that we don't
		/// actually use, but may be useful to folks, nontheless.
		/// </summary>
		[Test(Description = "Level 1 series a tests for Miscellaneous")]
		public void QATester_RunTest_Miscellaneous_a()
		{
			bool areEqual = false;
			////////// Check access to a wrapped Int32 through reflection on the BoxedValue.
			// Here we set the wrapped Int32 through reflection on it's box.
			m_rVTRInt32TRA1.InnerValue = 1;
			areEqual = ((IReferencedValueType<Int32>)m_rVTRInt32TRA1).IsValueSameAs(m_rVTRInt32TRA2);
			Assert.IsFalse(areEqual, "TestInt32.InnerValue is broken (not setting)");
			m_rVTRInt32TRA1.InnerValue = 0;
			areEqual = ((IReferencedValueType<Int32>)m_rVTRInt32TRA1).IsValueSameAs(m_rVTRInt32TRA2);
			Assert.IsTrue(areEqual, "TestInt32.InnerValue is broken (not resetting)");
		}
		#endregion // Quantitative Tests
	}
	/// <summary>
	/// Level 2 tests for functions in ReferencedValueType. These have long loops,
	/// but are not supposed to do allocations.
	/// </summary>
	[TestFixture]
	[Category(QAUtils.TestLevel2Name)]
	public class QATester_ReferencedValueType_2
	{
		#region Class Fields
		#endregion
		/// <summary>
		/// Obligatory default constructor.
		/// </summary>
		public QATester_ReferencedValueType_2()
		{
		}
		/// <summary>
		/// Placeholder.
		/// </summary>
		[TestFixtureSetUp]
		public void TestFixtureSetup(){	}
		/// <summary>
		/// Placeholder.
		/// </summary>
		[TestFixtureTearDown]
		public void TestFixtureTearDown(){ }
		#region Qualitative Tests
		///////////////////////////////////////////////////////////////////////
		// This section contains tests that print to the console to allow visual
		// inspection of results and run loops to check allocations with the
		// external "CLR Profiler" tool.
		///////////////////////////////////////////////////////////////////////
		/// <summary>
		/// This test demonstrates accessing the CIL RVT's.
		/// </summary>
		/// <remarks>
		/// ////////////////////////////////////////////////////////////////
		/// Memory allocation tests for accessing the structure - this should
		/// not create any new boxes - CIL version. Profiler should show about
		/// 100K or 200K bytes of allocations (MAX) and it should not depend on the
		/// loop count.
		/// ////////////////////////////////////////////////////////////////
		/// </remarks>
		[Test(Description = "Level 2 series a tests for CIL RVTs")]
		public void QATester_RunTest_CILRVT_a()
		{
			///////////////////////////////////////////////////////////////////
			// Basic tests for the IlUtils lib. - Note -- this is copied from
			// QATester_CILUtility fixme (when done with it)!.
			///////////////////////////////////////////////////////////////////
			//			IlUtils.Main(); // fixme remove this line (when done).

			//			RVT rVT; = null;
			//			SimpleStruct simpleStruct;
			//// Create new RVT with default values.
			//// This is the CIL version.
			//			rVT = new RVT();
			//			// Create new RVT with default values.
			//			// Pull out the structure by unboxing.
			//			simpleStruct = (SimpleStruct)rVT.boxedStruct;
			//			// Examine a public field.
			//			int testInt = simpleStruct.i1;
			//			Console.WriteLine("i1 = " + testInt.ToString());

			///////////////////////////////////////////////////////////////////
			// Basic tests for the RVT set method - set the internal struct
			// without boxing.
			///////////////////////////////////////////////////////////////////
			//			// Create a fresh SimpleStruct from it's parameterized constructor.
			//			simpleStruct = new SimpleStruct(12, 23);
			//			// Use RVT's Value set method to set the internal boxed value.
			//			rVT.set_Value(simpleStruct);

			//			// Look at it again.
			//			simpleStruct = (SimpleStruct)rVT.boxedStruct;
			//			testInt = simpleStruct.i1;
			//			Console.WriteLine("i1 = " + testInt.ToString());
			//			// Look at it again.
			//			simpleStruct = (SimpleStruct)rVT.boxedStruct;
			//			testInt = simpleStruct.i2;
			//			Console.WriteLine("i2 = " + testInt.ToString());
			///////////////////////////////////////////////////////////////////
			// Memory allocation tests for accessing the structure - this should
			// not create any new boxes - CIL version.
			///////////////////////////////////////////////////////////////////
			// Create new RVT with default values.
			// This is the CIL version.
			//rVT = new RVT();
			//SimpleStruct simpleStruct = new SimpleStruct(-1, -1);
			//for(int i = 0; i < 1000000; i++) {
			//     Create a fresh SimpleStruct from it's parameterized constructor.
			//    simpleStruct = new SimpleStruct(i, i + 1);
			//     Use RVT's Value set method to set the internal boxed value.
			//    rVT.set_Value(simpleStruct);

			//     Pull it out and look at it.
			//    simpleStruct = (SimpleStruct)rVT.boxedStruct;
			//                    Console.WriteLine(simpleStruct.i1.ToString());
		}
		/// <summary>
		/// This test demonstrates accessing the RVT's through the
		/// <see cref="IValueProp&lt;UValueType&gt;"/> interface.
		/// </summary>
		/// <remarks>
		/// ////////////////////////////////////////////////////////////////
		/// RVT's instantiated as IValueProp's closed as an Int32 (an Int32RVTVP).
		/// This is a memory test for the CLRProfiler.
		///
		/// When run under the profiler, this test should produce somewhere
		/// below 100K or 200K bytes of allocations (MAX) and it should not
		/// depend on the loop count.
		/// ////////////////////////////////////////////////////////////////
		/// </remarks>
		[Test(Description = "Level 2 series a tests for IValueProp RVTs")]
		public void QATester_RunTest_IVPRVT_a()
		{
			ReferencedValueType<Int32> m_Int32RVTVP1 = new Int32RVTVP(0);
			ReferencedValueType<Int32> m_Int32RVTVP2 = new Int32RVTVP(1);
			// Note this is the same Type as the previous lines. Just showing
			// that you can handle a closed Generic different ways.
			Int32RVTVP m_Int32RVTVP3 = new Int32RVTVP(2);
			for(Int32 i = 0; i < 1000000; i++) {
				// This should print once. The inequality check uses the facilities
				// within ReferencedValueType to inspect the internal ValueType.
				//// The following had to be commented out. It will be rereleased as
				//// soon as we get the full TypeHandling facilities converted and
				//// available. The problem is that the inequality calls into our
				//// TypeHandlingUtils.TypeHandlingUtils.DoesSecondTypeInheritFromFirstType
				//// method, whose implementation calls into MS's GetInterfaces() method
				//// which generates a fresh Type[] array every time - doesn't do
				//// any pooling. Our TypeHandlers cache the Interface array on the Type.
				//if(m_Int32RVTVP1 != m_Int32RVTVP2) {
				//    Console.WriteLine("Hey, we're not equal yet!!");
				//}
				// This line accesses the RVT's internal boxed value through the
				// IValueProp interface, avoiding BOXing.
				m_Int32RVTVP1.Value = m_Int32RVTVP2.Value;
				// Uncomment the next line if you want to see some awful allocations.
				// It should be around 20-40 MB or so with 1 million loop iterations.
				// Designed to show why you don't want to use an object-based equals
				// for your ValueType in Real-Time applications. What is happening
				// here is that we are reboxing every time we make the Equals call.
				// if(!Equals(m_Int32RVTVP1.Value, m_Int32RVTVP2.Value)) { }
			}
		}

		#endregion // Qualitative Tests
	}
	/// <summary>
	/// Level 5 tests for functions in ReferencedValueType. These have long loops,
	/// and do lots of allocations.
	/// </summary>
	[TestFixture]
	[Category(QAUtils.TestLevel5Name)]
	public class QATester_ReferencedValueType_5
	{
		#region Class Fields
		#endregion
		/// <summary>
		/// Obligatory default constructor.
		/// </summary>
		public QATester_ReferencedValueType_5()
		{
		}
		/// <summary>
		/// Placeholder.
		/// </summary>
		[TestFixtureSetUp]
		public void TestFixtureSetup(){	}
		/// <summary>
		/// Placeholder.
		/// </summary>
		[TestFixtureTearDown]
		public void TestFixtureTearDown(){ }
		#region Qualitative Tests
		///////////////////////////////////////////////////////////////////////
		// This section contains tests that print to the console to allow visual
		// inspection of results and run loops to check allocations with the
		// external "CLR Profiler" tool.
		///////////////////////////////////////////////////////////////////////
		/// <summary>
		/// This test demonstrates accessing the CSharp RVT's.
		/// </summary>
		/// <remarks>
		/// ////////////////////////////////////////////////////////////////
		/// Memory allocation tests for accessing the structure - this should
		/// create new BOXes - CSharp version. Profiler should show about 40MB
		/// or so allocated.
		/// ////////////////////////////////////////////////////////////////
		/// </remarks>
		[Test(Description = "Level 5 series a tests for CSharp RVTs")]
		public void QATester_RunTest_CSharpRVT_a()
		{
			///////////////////////////////////////////////////////////////////
			// Memory allocation tests for accessing the structure - this should
			// create new boxes (and bring your system to its knees!!)
			// - CSharp version.
			///////////////////////////////////////////////////////////////////
			// Create new RVT with default values.
			//// This is the CSharp version.
			RVTCSharp rVT = new RVTCSharp();
			SimpleStructCSharp simpleStruct = new SimpleStructCSharp(-1, -1);
			for(int i = 0; i < 1000000; i++) {
				// Create a fresh SimpleStruct from it's parameterized constructor.
				simpleStruct = new SimpleStructCSharp(i, i + 1);
				// Use RVT's Value set method to set the internal boxed value.
				////// Style 1 - implicit accessors.
				rVT.Value = simpleStruct;
				rVT.Value = rVT.Value;
				////// End Style 1.
				////// Style 2 - explicit accessors.
				// rVT.set_Value(simpleStruct);
				// rVT.set_Value(rVT.get_Value());
				////// End Style 2.

				// Pull it out and look at it.
				simpleStruct = (SimpleStructCSharp)rVT.boxedStruct;
				//				Console.WriteLine(simpleStruct.i1.ToString());
			}
		}
		#endregion // Qualitative Tests
	}
	#endregion // Tests

	#region Some Example and Test Types
	///////////////////////////////////////////////////////////////////////////
	//// This region contains a few simple Types that are used for testing and
	//// tutorial purposes.
	///////////////////////////////////////////////////////////////////////////
	#region Test Types for CSharp Implementations of RVT's
	///////////////////////////////////////////////////////////////////////////
	//// This is designeded to be a set of Types to show the BOXing problem at
	//// the most elemental level to show the difference between CSharp and CIL.
	///////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// Class containing a CSharp struct that will be accessed through a BOX,
	/// thus undergoing BOXing.
	/// </summary>
	public class RVTCSharp
	{
		/// <summary>
		/// A ValueType reference to a BOXed SimpleStructCSharp.
		/// </summary>
		public System.ValueType boxedStruct;
		/// <summary>
		/// Constructor just embeds a default struct.
		/// </summary>
		public RVTCSharp() { boxedStruct = new SimpleStructCSharp(); }
		/// <summary>
		/// Constructor embeds a default struct, then sets it's fields.
		/// </summary>
		/// <param name="i1">First Int32.</param>
		/// <param name="i2">Second Int32.</param>
		public RVTCSharp(Int32 i1, Int32 i2)
		{
			SimpleStructCSharp workingStruct = new SimpleStructCSharp();
			workingStruct.i1 = i1;
			workingStruct.i2 = i2;
			// COPY the struct into its HOME in the BOX.
			boxedStruct = workingStruct;
		}

		//// The following method is what Roeder decompiles our CIL version
		//// of set_Value into. Note that this method will not compile. We get:
		//// "Error	853	Cannot modify the result of an unboxing conversion."
		//// This goes to show that you can't always trust decompilers!! If
		//// it compiled, we would not have had to use CIL in the first
		//// place !!!!
		////
		//// The problem is, of course, that silly CSharp always translates
		//// any cast from a BOXed ValueType to an unBOXed HOME to the
		//// equivalent of an "unbox.any" CIL OP.
		//public void set_Value(SimpleStruct simpleStruct)
		//{
		//    ((SimpleStruct)this.boxedStruct).i2 = simpleStruct.i2;
		//    ((SimpleStruct)this.boxedStruct).i1 = simpleStruct.i1;
		//}
		/////////////////////
		/////// Style 1 - Implicit accessors.
		/// <summary>
		/// Get/Set the internal struct as a normal C# Property.
		/// </summary>
		public SimpleStructCSharp Value
		{
			get { return (SimpleStructCSharp)this.boxedStruct; }
			// This is the way we must do it in C# - rebox everytime...
			set { this.boxedStruct = value; }

		}
		/////// End Style 1.

		/////// Style 2 - explicit accessors.
		//public void set_Value(SimpleStructCSharp simpleStruct)
		//{
		//    // This is the way we must do it in C# - rebox everytime...
		//    this.boxedStruct = simpleStruct;
		//}
		//public SimpleStructCSharp get_Value()
		//{
		//    return (SimpleStructCSharp)this.boxedStruct;
		//}
		/////// End Style 2.
	}
	/// <summary>
	/// Another simple Type. This is a struct with two public and one private int.
	/// </summary>
	public struct SimpleStructCSharp
	{
		/// <summary>
		/// First one.
		/// </summary>
		public Int32 i1;
		/// <summary>
		/// Second one.
		/// </summary>
		public Int32 i2;
		/// <summary>
		/// This one is private. Can't access this one from C# or CIL either.
		/// </summary>
		private Int32 i3;

		/// <summary>
		/// Constructor that sets public fields from args and sets private to default.
		/// </summary>
		/// <param name="i1">First Int32.</param>
		/// <param name="i2">Second Int32.</param>
		public SimpleStructCSharp(Int32 i1, Int32 i2)
		{
			this.i1 = i1;
			this.i2 = i2;

			// Unlike in CIL, where the "initlocals" keyword does the job, all
			// fields must be explicitly initialized.
			this.i3 = 0;
		}

		/// <summary>
		/// Set method.
		/// </summary>
		/// <param name="simpleStructCSharp">
		/// The value to set the struct to.
		/// </param>
		public void set_Value(SimpleStructCSharp simpleStructCSharp)
		{
			i1 = simpleStructCSharp.i1;
			i2 = simpleStructCSharp.i2;
		}
	}
	#endregion // Test Types for CSharp Implementations of RVT's
	#endregion // Some Example and Test Types
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
Kurt R. Matis received the B.S degree in Applied Mathemetics from Empire State College in 1981 and the PhD. degree in Electrical Engineering from Rensselaer Polytechnic Institute in 1984. He has been involved in several companies over the past 30 years, but has been most recently involved with the Macher-Plander Software Engineering Consortium, of which he is a co-founder. The Consortium is involved with education in .Net technologies and Software Quality Management topics.

Dr. Matis is a member of IEEE and the American Historical Truck Society. Kurt lives happily in Troy, NY with his beautiful wife, two beautiful daughters and his beautiful trucks.

Dr. Matis is interested in working with companies who wish assistance in porting legacy applications of all types to .Net. He can be reached at krogerma@aol.com.




Comments and Discussions