Is it possible to disable run time (and compile time) checks on a class using a method call or similar? I am having trouble with my classes with invariants and using them with external libraries that construct instances dynamically. I would like to wrap those calls inside my own calls, which take a potentially partially constructed object, and returns one that is always valid.
Eg consider this code:
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
namespace ConsoleApp
{
class Person
{
public string Name { get; set; }
public string Email { get; set; }
public string JobTitle { get; set; }
public Person(string name, string email, string title)
{
Name = name;
Email = email;
JobTitle = title;
}
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(Name != null);
Contract.Invariant(JobTitle != null);
}
}
public static class ObjectBuilder
{
// Just a sample method for building an object dynamically. In my actual code, code using
// elastic search, NEST, serializion or entity framework has similar problems.
public static T BuildFromDictionary<T>(Dictionary<string, object> dict)
{
Contract.Requires(dict != null);
T result = (T)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof (T));
foreach (var pair in dict)
{
string propName = pair.Key;
var property = typeof (T).GetProperty(propName);
property.SetValue(result, pair.Value);
}
return result;
}
}
class Program
{
public static Person CreatePerson()
{
Dictionary<string, object> personData = new Dictionary<string, object>();
personData["Name"] = "Fred";
personData["Email"] = "[email protected]";
Person person = ObjectBuilder.BuildFromDictionary<Person>(personData);
person.JobTitle = "Programmer";
return person;
}
static void Main(string[] args)
{
Person person1 = new Person("Bob", "[email protected]", "Hacker");
Person person = CreatePerson();
Console.WriteLine(person.Name);
}
}
}
This compiles correctly, however will throw an exception on the property.SetValue(result, pair.Value);
line. This is because it will call the Name setter, and at that stage Email
and JobTitle
are null.
What I would like to do is to disable contracts within a code section. Eg, replacing the CreatePerson
method with something like this:
public static Person CreatePerson()
{
Dictionary<string, object> personData = new Dictionary<string, object>();
personData["Name"] = "Fred";
personData["Email"] = "[email protected]";
Person person;
Contract.DisableRunTimeChecks(() =>
{
person = ObjectBuilder.BuildFromDictionary<Person>(personData);
person.JobTitle = "Programmer";
});
Contract.CheckInvariants(person);
return person;
}
Where Contract.DisableRunTimeChecks
disables run time checks for all code within that code block (and any calls made within), and Contract.CheckInvariants
runs the ContractInvariantMethod
for the given object.
Is this possible to implement somehow, or is there another solution?
One solution I have seen which I don't want to do is to introduce an _initialized
field on Person
, and making the invariant method check if that is true before doing any checks. This is because the object should always be valid except for one or two of these constructor methods.
The problem is with the way the invariants are defined. I posted an answer to a similar question here. Please read the answer to the question for the details, but here's a quick couple of paragraphs from that answer that's at the root of the issue here:
So here's how you'll want to define the
Person
class:So to recap: Invariants only tell consumers what they can expect to be true about the object (at runtime) prior to calling any public method and upon any return from a public method. In this case, you're telling the consumer that you guarantee when they call a public method that
Person.Name
andPerson.JobTitle
will be non-null
, and that when any public method returns to the caller, again,Person.Name
andPerson.JobTitle
will be non-null
. However, in order to ensure that these invariants can be maintained (and enforced), the class needs to state the pre-conditions and post-conditions when getting/setting the properties which mutate the values of the private backing fields_name
and_jobTitle
.