Nullable and the struct constraint in VS.NET Beta 2
In VS.NET 2005 Beta 1 it was possible to define a variable of type
Nullable<Nullable<Nullable<int>>> number;
and, therefore,
int??? number;
Nullable<T> only allows type parameters that are value types through the use of the struct constraint as follows:
public struct Nullable<T> :
IFormattable, IComparable,
IComparable<Nullable<T>>, INullable
where T : struct
{
// ...
}
In Beta 1 this still allowed a T type parameter of Nullable<T>, however, because (as is shown above) Nullable<T> was a struct.
In Beta 2, the meaning of the struct constraint has improved to avoid this. Now the struct constraint only allows value types except System.Nullable<T>. For example, the following code is prohibited:
class AClass<T>
where T : struct
{
public void Method()
{
// ERROR: The type 'int?' must be a non-nullable
// value type in order to use it as parameter
// 'T' in the generic type or method
AClass<int?> trial;
}
}
Thus avoiding the problem described earlier.
Wednesday, May 11, 2005 2:07:44 PM (Pacific Standard Time, UTC-08:00)
Computer Related | .Net
Registering a string of code as an apparent "delegate" listener
In my previous post I described how to provide custom validation code at runtime. I register the code through a call to RegisterNameValidationCode(). One variation on this is to provide a += operator to register the code so that it looks as though I am registering a delegate.
bool exceptionFired = false;
Person person = new Person();
person.NameValidationCode +=
@"public static void OnNameChanged(object sender, PropertyValidation.OnBeforeNameChangeEventArgs args)
{
if (args.After == null)
{
throw new System.ArgumentNullException();
}
}";
try
{
person.Name = null;
}
catch(ArgumentNullException)
{
exceptionFired = true;
}
Trace.Assert(exceptionFired);
As shown, registering the validation code just as one would a delegate, you use the += operator:
To support this, I defined a new type, ValidationCode:
public class ValidationCode
{
public string Code;
public ValidationCode(string code)
{
Code = code;
}
public static string operator +(ValidationCode code)
{
return code.Code;
}
public static implicit operator ValidationCode(string code)
{
return new ValidationCode(code);
}
}
They type includes operator overloading for an implicit cast and the + operator. You can't override any assignment operators including the += operator. However, if the assignment includes a cast then you can effectively overload the assignment operator by providing an implicit cast.
Next, I add ValidationCode as a property on Person:
public class Person
{
// ...
public ValidationCode NameValidationCode
{
set
{
_NameValidationCode = value;
RegisterNameValidationCode("OnNameChanged", value.Code);
}
get
{
return _NameValidationCode;
}
}
private ValidationCode _NameValidationCode;
}
Although this allowed for calling code (see the top listing in this post) that looks just like delegate registration would look with a operator +=. Note that there is no intellisense indicating that a string can be assigned, not just a ValidationCode instance. In other words, this would not be an intuitive way to code the registration. The other drawback, is that I hard code the method name inside the NameValidationCode property. Alternatives would be to parse the code and retrieve the method name or simply not provide the method declaration in the string at all, and rather inject it before compiling.
(The full source code is provided in the previous post.)
Tuesday, May 10, 2005 10:56:25 AM (Pacific Standard Time, UTC-08:00)
Computer Related | .Net
VSLive Las Vegas: Compiling code at runtime
I was asked to post my source code for my VSLive-LasVegas talk, "Compiling code at runtime", so here it is.
In my session, I outlined a scenario for adding custom validation to a property at runtime. To support this, the Name property on Person fired an event every time a new name was assigned - as shown here:
public class Person
{
public event OnNameChangeHandler OnBeforeNameChange;
public string Name
{
get { return _Name; }
set
{
OnNameChangeHandler changeEvent = OnBeforeNameChange;
if (changeEvent != null)
{
OnBeforeNameChangeEventArgs args =
new OnBeforeNameChangeEventArgs(_Name, value);
changeEvent(this, args);
}
_Name = value;
}
}
private string _Name;
}
With this code, a developer could register a delegate that threw an exception whenever an invalid name was provided. However, to allow the validation to be runtime configurable, I needed the ability to provide a string (not a delegate) that the user could provide. (The user in this case is a VBA programmer type, not an AOL end users type.) To support this I added a RegisterNameValidationCode() method with client code as follows:
bool exceptionFired = false;
Person person = new Person();
person.RegisterNameValidationCode("OnNameChanged",
@"public static void OnNameChanged(object sender, PropertyValidation.OnBeforeNameChangeEventArgs args)
{
if (args.After == null)
{
throw new System.ArgumentNullException();
}
}");
try
{
person.Name = null;
}
catch(ArgumentNullException)
{
exceptionFired = true;
}
Trace.Assert(exceptionFired);
In this case the validation code is a hard coded string but the idea would obviously be to allow it to be user entered. C# is an arbitrary choice, using VB would be just as easy.
The RegisterNameValidationCode() function is shown below.
public class Person
{
// ...
public void RegisterNameValidationCode(string methodName, string code)
{
CompilerParameters options = new CompilerParameters();
string typeName = "_" + Guid.NewGuid().ToString("N");
options.ReferencedAssemblies.Add(GetType().Assembly.Location );
options.GenerateInMemory = true;
code = string.Format(
@"using System;
public class {0}
{{
{1}
}}",
typeName, code);
CompilerResults compilerResults =
new CSharpCodeProvider().CompileAssemblyFromSource(options,
new string[] { code });
if (compilerResults.Errors.HasErrors)
{
string[] output = new string[compilerResults.Output.Count];
compilerResults.Output.CopyTo(output, 0);
throw new ApplicationException(string.Join(
Environment.NewLine, output));
}
// Use reflection to obtain a handle to the newly compiled method
Type type = compilerResults.CompiledAssembly.GetType(typeName);
MethodInfo methodInfo =
type.GetMethod("OnNameChanged");
OnBeforeNameChange += (OnNameChangeHandler)Delegate.CreateDelegate(
typeof(OnNameChangeHandler), methodInfo);
}
}
It is pretty straight forward except at the end where it hooks up a delegate to a MethodInfo object at runtime.
One additional piece that I ran out of time to cover is the ability to use the += operator to register the validation string just as though it were a delegate. I'll describe this in my next post but the code is included in this post's attachment/enclosure.
Tuesday, May 10, 2005 10:38:53 AM (Pacific Standard Time, UTC-08:00)
Computer Related | .Net
CodeCompilationAtRuntime.zip (7.77 KB)