Given the following implementation of mutable and immutable types, is there a way to avoid duplicate code (mainly the duplicate properties)?
I'd like to work with immutable type by default unless a mutable type is required (e.g. when binding to UI elements).
We're using .NET framework 4.0, but plan switching to 4.5 soon.
public class Person {
public string Name { get; private set; }
public List<string> Jobs { get; private set; } // Change to ReadOnlyList<T>
public Person() {}
public Person(Mutable m) {
Name = m.Name;
}
public class Mutable : INotifyPropertyChanged {
public string Name { get; set; }
public List<string> Jobs { get; set; }
public Mutable() {
Jobs = new List<string>();
}
public Mutable(Person p) {
Name = p.Name;
Jobs = new List<string>(p.Jobs);
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) {
// TODO: implement
}
}
}
public class Consumer {
public Consumer() {
// We can use object initializers :)
Person.Mutable m = new Person.Mutable {
Name = "M. Utable"
};
// Consumers can happily mutate away....
m.Name = "M. Utated";
m.Jobs.Add("Herper");
m.Jobs.Add("Derper");
// But the core of our app only deals with "realio-trulio" immutable types.
// Yey! Have constructor with arity of one as opposed to
// new Person(firstName, lastName, email, address, im, phone)
Person im = new Person(m);
}
}
I made something that does what you ask recently (using T4 templates), so it is absolutely possible:
https://github.com/xaviergonz/T4Immutable
For example, given this:
[ImmutableClass(Options = ImmutableClassOptions.IncludeOperatorEquals)]
class Person {
private const int AgeDefaultValue = 18;
public string FirstName { get; }
public string LastName { get; }
public int Age { get; }
[ComputedProperty]
public string FullName {
get {
return FirstName + " " + LastName;
}
}
}
It will automatically generate for you in a separate partial class file the following:
A constructor such as public Person(string firstName, string
lastName, int age = 18) that will initialize the values.
Working implementations for Equals(object other) and Equals(Person other).
Also it will add the IEquatable interface for you. Working
implementations for operator== and operator!=
A working implementation of GetHashCode() A better ToString() with output such as "Person { FirstName=John, LastName=Doe, Age=21 }"
A Person With(...) method that can be used to generate a new immutable clone with 0 or more properties changed (e.g. var janeDoe = johnDoe.With(firstName: "Jane", age: 20)
So it will generate this (excluding some redundant attributes):
using System;
partial class Person : IEquatable<Person> {
public Person(string firstName, string lastName, int age = 18) {
this.FirstName = firstName;
this.LastName = lastName;
this.Age = age;
_ImmutableHashCode = new { this.FirstName, this.LastName, this.Age }.GetHashCode();
}
private bool ImmutableEquals(Person obj) {
if (ReferenceEquals(this, obj)) return true;
if (ReferenceEquals(obj, null)) return false;
return T4Immutable.Helpers.AreEqual(this.FirstName, obj.FirstName) && T4Immutable.Helpers.AreEqual(this.LastName, obj.LastName) && T4Immutable.Helpers.AreEqual(this.Age, obj.Age);
}
public override bool Equals(object obj) {
return ImmutableEquals(obj as Person);
}
public bool Equals(Person obj) {
return ImmutableEquals(obj);
}
public static bool operator ==(Person a, Person b) {
return T4Immutable.Helpers.AreEqual(a, b);
}
public static bool operator !=(Person a, Person b) {
return !T4Immutable.Helpers.AreEqual(a, b);
}
private readonly int _ImmutableHashCode;
private int ImmutableGetHashCode() {
return _ImmutableHashCode;
}
public override int GetHashCode() {
return ImmutableGetHashCode();
}
private string ImmutableToString() {
var sb = new System.Text.StringBuilder();
sb.Append(nameof(Person) + " { ");
var values = new string[] {
nameof(this.FirstName) + "=" + T4Immutable.Helpers.ToString(this.FirstName),
nameof(this.LastName) + "=" + T4Immutable.Helpers.ToString(this.LastName),
nameof(this.Age) + "=" + T4Immutable.Helpers.ToString(this.Age),
};
sb.Append(string.Join(", ", values) + " }");
return sb.ToString();
}
public override string ToString() {
return ImmutableToString();
}
private Person ImmutableWith(T4Immutable.WithParam<string> firstName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<string> lastName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<int> age = default(T4Immutable.WithParam<int>)) {
return new Person(
!firstName.HasValue ? this.FirstName : firstName.Value,
!lastName.HasValue ? this.LastName : lastName.Value,
!age.HasValue ? this.Age : age.Value
);
}
public Person With(T4Immutable.WithParam<string> firstName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<string> lastName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<int> age = default(T4Immutable.WithParam<int>)) {
return ImmutableWith(firstName, lastName, age);
}
}
And there are some more features as explained in the project page.
PS: If you want a property that's a list of other immutable objects just add:
public ImmutableList<string> Jobs { get; }
No, there's no easy way to avoid duplicate code.
What you've implemented is effectivly the builder pattern. The .NET StringBuilder class follows the same approach.
The support for immutable types in C# is a bit lacking, and could do with some language specific features to make it easier. Having to create a builder is a real pain, as you've discovred. An alternative is to have a constructor that takes all the values, but you tend to end up with the mother of all constructors, which makes the code unreadable.
Since the properties don't have the same visibility, this is not duplicate code. If their visibilty were the same, Person could inherit from Mutable to avoid duplication. Right now, I don't think there is code to factorize in what you show.
Think about using code generation to map each mutable to its immutable equivalent.
I personally like T4 code generation, aided by T4Toolbox library.
You can quite easily parse your code using EnvDTE.
You can find tons of high-quality information about T4 on Oleg Sych blog
http://www.olegsych.com/
Code generation could be hard to handle in the beginning, but it solves the infamous issue of the code-that-must-be-duplicated.
One for your consideration depending on if you're creating a public facing API is to consider 'popcicle immutability' as discussed by Eric Lippert. The nice thing about this is you don't need any duplication at all.
I've used something in reverse, my classes are mutable until a certain point when some calculations are going to occur at which point I call a Freeze() method. All changes to properties call a BeforeValueChanged() method, which if frozen throws an exception.
What you need is something whereby the classes are frozen by default, and you unfreeze them if you need them mutable. As others have mentioned if frozen you need to be returning read only copies of lists etc.
Here's an example of a little class I put together:
/// <summary>
/// Defines an object that has a modifiable (thawed) state and a read-only (frozen) state
/// </summary>
/// <remarks>
/// All derived classes should call <see cref="BeforeValueChanged"/> before modifying any state of the object. This
/// ensures that a frozen object is not modified unexpectedly.
/// </remarks>
/// <example>
/// This sample show how a derived class should always use the BeforeValueChanged method <see cref="BeforeValueChanged"/> method.
/// <code>
/// public class TestClass : Freezable
/// {
/// public String Name
/// {
/// get { return this.name; }
/// set
/// {
/// BeforeValueChanged();
/// this.name = name;
/// }
/// }
/// private string name;
/// }
/// </code>
/// </example>
[Serializable]
public class Freezable
{
#region Locals
/// <summary>Is the current instance frozen?</summary>
[NonSerialized]
private Boolean _isFrozen;
/// <summary>Can the current instance be thawed?</summary>
[NonSerialized]
private Boolean _canThaw = true;
/// <summary>Can the current instance be frozen?</summary>
[NonSerialized]
private Boolean _canFreeze = true;
#endregion
#region Properties
/// <summary>
/// Gets a value that indicates whether the object is currently modifiable.
/// </summary>
/// <value>
/// <c>true</c> if this instance is frozen; otherwise, <c>false</c>.
/// </value>
public Boolean IsFrozen
{
get { return this._isFrozen; }
private set { this._isFrozen = value; }
}
/// <summary>
/// Gets a value indicating whether this instance can be frozen.
/// </summary>
/// <value>
/// <c>true</c> if this instance can be frozen; otherwise, <c>false</c>.
/// </value>
public Boolean CanFreeze
{
get { return this._canFreeze; }
private set { this._canFreeze = value; }
}
/// <summary>
/// Gets a value indicating whether this instance can be thawed.
/// </summary>
/// <value>
/// <c>true</c> if this instance can be thawed; otherwise, <c>false</c>.
/// </value>
public Boolean CanThaw
{
get { return this._canThaw; }
private set { this._canThaw = value; }
}
#endregion
#region Methods
/// <summary>
/// Freeze the current instance.
/// </summary>
/// <exception cref="System.InvalidOperationException">Thrown if the instance can not be frozen for any reason.</exception>
public void Freeze()
{
if (this.CanFreeze == false)
throw new InvalidOperationException("The instance can not be frozen at this time.");
this.IsFrozen = true;
}
/// <summary>
/// Does a Deep Freeze for the duration of an operation, preventing it being thawed while the operation is running.
/// </summary>
/// <param name="operation">The operation to run</param>
internal void DeepFreeze(Action operation)
{
try
{
this.DeepFreeze();
operation();
}
finally
{
this.DeepThaw();
}
}
/// <summary>
/// Applies a Deep Freeze of the current instance, preventing it be thawed, unless done deeply.
/// </summary>
internal void DeepFreeze()
{
// Prevent Light Thawing
this.CanThaw = false;
this.Freeze();
}
/// <summary>
/// Applies a Deep Thaw of the current instance, reverting a Deep Freeze.
/// </summary>
internal void DeepThaw()
{
// Enable Light Thawing
this.CanThaw = true;
this.Thaw();
}
/// <summary>
/// Thaws the current instance.
/// </summary>
/// <exception cref="System.InvalidOperationException">Thrown if the instance can not be thawed for any reason.</exception>
public void Thaw()
{
if (this.CanThaw == false)
throw new InvalidOperationException("The instance can not be thawed at this time.");
this.IsFrozen = false;
}
/// <summary>
/// Ensures that the instance is not frozen, throwing an exception if modification is currently disallowed.
/// </summary>
/// <exception cref="System.InvalidOperationException">Thrown if the instance is currently frozen and can not be modified.</exception>
protected void BeforeValueChanged()
{
if (this.IsFrozen)
throw new InvalidOperationException("Unable to modify a frozen object");
}
#endregion
}
Related
I am trying to binary format a list of Animals(either a cat or a dog). This goes well to some extend. Ive made use of the ISerialize interface in my base class, Animal. The two derived classes, dog and cat, however both have 1 extra property the base class doesn't have. Now, the serialization and deserialization of the base class animal gives me no problems. The problem starts when i try to serialize and deserialize either one of those extra properties from the derived classes I mentioned earlier. I feel like there is an easy fix to this but I am overseeing something.
Below I will post code of the following locations:
The ISerialize interface in the Animal class
The ISerialize interface in the Dog class
The ISerialize interface in the Cat class
The implementation of the serializer and deserializer in the Administration class (both are bound to two buttons in a form, so i can execute them)
The error Visual Studio throws at me (translation of the dutch text: "Member BadHabits is not found")
I thank you in advance for any remarks and/or solutions to this problem.
[Serializable()]
abstract public class Animal : ISellable, IComparable<Animal>, ISerializable
{
/// <summary>
/// The chipnumber of the animal. Must be unique. Must be zero or greater than zero.
/// </summary>
public int ChipRegistrationNumber { get; private set; }
public abstract decimal Price { get; }
/// <summary>
/// Date of birth of the animal.
/// </summary>
public SimpleDate DateOfBirth { get; private set; }
/// <summary>
/// The name of the animal.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Is the animal reserved by a future owner yes or no.
/// </summary>
public bool IsReserved { get; set; }
/// <summary>
/// Creates an animal.
/// </summary>
/// <param name="chipRegistrationNumber">The chipnumber of the animal.
/// Must be unique. Must be zero or greater than zero.</param>
/// <param name="dateOfBirth">The date of birth of the animal.</param>
/// <param name="name">The name of the animal.</param>
public Animal(int chipRegistrationNumber, SimpleDate dateOfBirth, string name)
{
if (chipRegistrationNumber < 0)
{
throw new ArgumentOutOfRangeException("Chipnummer moet groter of gelijk zijn aan 0");
}
ChipRegistrationNumber = chipRegistrationNumber;
DateOfBirth = dateOfBirth;
if (name == null)
{
throw new ArgumentNullException("Naam is null");
}
Name = name;
IsReserved = false;
}
/// <summary>
/// Retrieve information about this animal
///
/// Note: Every class inherits (automagically) from the 'Object' class,
/// which contains the virtual ToString() method which you can override.
/// </summary>
/// <returns>A string containing the information of this animal.
/// The format of the returned string is
/// "<ChipRegistrationNumber>, <DateOfBirth>, <Name>, <IsReserved>"
/// Where: IsReserved will be "reserved" if reserved or "not reserved" otherwise.
/// </returns>
public override string ToString()
{
string IsReservedString;
if (IsReserved)
{
IsReservedString = "reserved";
}
else
{
IsReservedString = "not reserved";
}
string info = ChipRegistrationNumber
+ ", " + DateOfBirth
+ ", " + Name
+ ", " + IsReservedString;
return info;
}
private Animal(SerializationInfo info, StreamingContext context)
{
ChipRegistrationNumber = (Int32)info.GetValue("Chipnumber", typeof(Int32));
DateOfBirth = (SimpleDate)info.GetValue("Date of Birth", typeof(SimpleDate));
Name = (String)info.GetValue("Name", typeof(String));
IsReserved = (bool)info.GetValue("Isreserved", typeof(bool));
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Chipnumber", ChipRegistrationNumber);
info.AddValue("Date of Birth", DateOfBirth);
info.AddValue("Name", Name);
info.AddValue("Isreserved", IsReserved);
}
public int CompareTo(Animal other)
{
throw new NotImplementedException();
}
}
dog
[Serializable]
public class Dog : Animal
{
/// <summary>
/// The date of the last walk of the dog. Contains null if unknown.
/// </summary>
public SimpleDate LastWalkDate { get; set; }
/// <summary>
/// Creates a dog.
/// </summary>
/// <param name="chipRegistrationNumber">The chipnumber of the animal.
/// Must be unique. Must be zero or greater than zero.</param>
/// <param name="dateOfBirth">The date of birth of the animal.</param>
/// <param name="name">The name of the animal.</param>
/// <param name="lastWalkDate">The date of the last walk with the dog or null if unknown.</param>
public Dog(int chipRegistrationNumber, SimpleDate dateOfBirth,
string name, SimpleDate lastWalkDate) : base(chipRegistrationNumber, dateOfBirth, name)
{
if (lastWalkDate != null)
{
this.LastWalkDate = lastWalkDate;
}
}
/// <summary>
/// Retrieve information about this dog
///
/// Note: Every class inherits (automagically) from the 'Object' class,
/// which contains the virtual ToString() method which you can override.
/// </summary>
/// <returns>A string containing the information of this animal.
/// The format of the returned string is
/// "Dog: <ChipRegistrationNumber>, <DateOfBirth>, <Name>, <IsReserved>, <LastWalkDate>"
/// Where: IsReserved will be "reserved" if reserved or "not reserved" otherwise.
/// LastWalkDate will be "unknown" if unknown or the date of the last doggywalk otherwise.
/// </returns>
public override string ToString()
{
// TODO: Put your own code here to make the method return the string specified in the
// method description.
if (LastWalkDate == null)
{
return $"Dog: {base.ToString()} - unknown";
}
return $"Dog: {base.ToString()}, {LastWalkDate}";
}
public override decimal Price
{
get
{
if(ChipRegistrationNumber < 50000)
{
return 200;
}
return 350;
}
}
public new void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Lastwalk", LastWalkDate);
}
public Dog(SerializationInfo info, StreamingContext context) : base(info, context)
{
LastWalkDate = (SimpleDate)info.GetValue("Lastwalk", typeof(SimpleDate));
}
}
cat
[Serializable]
public class Cat : Animal
{
/// <summary>
/// Description of the bad habits that the cat has (e.g. "Scratches the couch").
/// or null if the cat has no bad habits.
/// </summary>
public string BadHabits { get; set; }
/// <summary>
/// Creates a cat.
/// </summary>
/// <param name="chipRegistrationNumber">The chipnumber of the animal.
/// Must be unique. Must be zero or greater than zero.</param>
/// <param name="dateOfBirth">The date of birth of the animal.</param>
/// <param name="name">The name of the animal.</param>
/// <param name="badHabits">The bad habbits of the cat (e.g. "scratches the couch")
/// or null if none.</param>
public Cat(int chipRegistrationNumber, SimpleDate dateOfBirth,
string name, string badHabits) : base(chipRegistrationNumber, dateOfBirth, name)
{
this.BadHabits = badHabits;
}
/// <summary>
/// Retrieve information about this cat
///
/// Note: Every class inherits (automagically) from the 'Object' class,
/// which contains the virtual ToString() method which you can override.
/// </summary>
/// <returns>A string containing the information of this animal.
/// The format of the returned string is
/// "Cat: <ChipRegistrationNumber>, <DateOfBirth>, <Name>, <IsReserved>, <BadHabits>"
/// Where: IsReserved will be "reserved" if reserved or "not reserved" otherwise.
/// BadHabits will be "none" if the cat has no bad habits, or the bad habits string otherwise.
/// </returns>
public override string ToString()
{
// TODO: Put your own code here to make the method return the string specified in the
// method description.
if (String.IsNullOrEmpty(BadHabits))
{
BadHabits = "none";
}
return $"{"Cat: "}{base.ToString()}, {BadHabits}";
}
public override decimal Price
{
get
{
int korting = BadHabits.Length - 60;
if ( korting > 20)
{
return korting;
}
return 20;
}
}
public new void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("BadHabits", this.BadHabits);
}
public Cat(SerializationInfo info, StreamingContext context) : base(info, context)
{
this.BadHabits = (String)info.GetString("BadHabits");
}
}
Administration
public void Save(String fileName)
{
Stream fileStream = File.Open(fileName, FileMode.Create);
BinaryFormatter format = new BinaryFormatter();
format.Serialize(fileStream, Animals);
fileStream.Close();
}
public void Load(String fileName)
{
FileStream fileStream;
BinaryFormatter format = new BinaryFormatter();
fileStream = File.OpenRead(fileName);
Animals = (List<Animal>)format.Deserialize(fileStream);
fileStream.Close();
}
Error message
The problem is the "new" part in your child method. By writing "new" on child class, implementation of inherited methods are going to be hidden.
Constructors are written properly, methods are the problem.
Here is one way you can fix this:
class Animal
{
...
public virtual GetObjectData(... params ...)
{
// populate properties
}
...
}
class Dog
{
public override GetObjectData(... params ...)
{
base.GetObjectData(... params ...)
// populate additional Dog properties
}
}
Following the book "Artificial Intelligence - A Modern Approach" I am trying to make search algorithm implementations (DFS, A*, etc.) that can work with different problems (path from A to B, sliding-block puzzle, etc.) instead of just the one specific problem.
public abstract class Problem
{
protected Problem(object initialState)
{
InitialState = initialState;
}
public object InitialState { get; }
/// <summary>
/// Checks if the state is the goal state
/// </summary>
public abstract bool IsGoalState(object state);
/// <summary>
/// Returns the actions available from the state
/// </summary>
public abstract ISet<Action> Actions(object state);
/// <summary>
/// Returns the state that results after performing the action on the state
/// </summary>
public abstract object ResultState(object state, Action action);
/// <summary>
/// Returns the cost of action to reach from state to reachedState
/// </summary>
/// <param name="state">The state from which the action will be performed</param>
/// <param name="action">One of the actions available in the state</param>
/// <param name="reachedState">The state that results after performing the action</param>
public virtual int StepCost(object state, Action action, object reachedState)
{
return 1;
}
}
_
public class Node
{
public Node(object state)
{
State = state;
PathCost = 0;
}
/// <summary>
/// </summary>
/// <param name="action">The action that was applied to the parent to generate the node</param>
/// <param name="stepCost">The cost from the parent node to this node</param>
public Node(object state, Node parent, Action action, int stepCost)
: this(state)
{
Parent = parent;
Action = action;
if (Parent != null)
PathCost = Parent.PathCost + stepCost;
}
public object State { get; }
public Node Parent { get; }
/// <summary>
/// The action that was applied to the parent to generate the node
/// </summary>
public Action Action { get; }
/// <summary>
/// The cost of the path from the initial statee to the node
/// </summary>
public int PathCost { get; }
public bool IsRootNode => Parent == null;
public IEnumerable<Node> PathFromRoot()
{
var path = new Stack<Node>();
var node = this;
while (!node.IsRootNode)
{
path.Push(node);
node = node.Parent;
}
path.Push(node); // root
return path;
}
}
_
public abstract class Action
{
/// <summary>
/// true if it is a "No Operation" action
/// </summary>
public virtual bool IsNoOp()
{
return false;
}
public string Name => GetType().Name;
public override string ToString()
{
return Name;
}
}
public class NoOp : Action
{
public override bool IsNoOp()
{
return true;
}
}
public abstract class EightPuzzleAction : Action
{
}
/// <summary>
/// Move the blank tile to the left
/// </summary>
public class MoveLeft : EightPuzzleAction
{
}
// the same MoveRight, MoveTop, MoveBottom
In order to do that I can implement methods from this base Problem class and pass that concrete instance to a search algorithm (that accepts Problem).
class EightPuzzleProblem : Problem
{
private readonly int[,] _goalState =
{
{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
};
public EightPuzzleProblem(int[,] initialState) : base(initialState)
{ }
public override bool IsGoalState(object state)
{
var puzzleState = (int[,]) state;
......... <check if puzzleState is the same as _goalState>
}
............
}
class DepthFirstSearch
{
public IEnumerable<Action> Search(Problem p)
{
return DfsRecursive(p, new Node(p.InitialState));
}
public IEnumerable<Action> DfsRecursive(Problem p, Node node)
{
if (p.IsGoalState(node.State))
return node.PathFromRoot();
.......<simple DFS implementation>
}
}
// usage example
var dfs = new DepthFirstSearch();
var result = dfs.Search(new EightPuzzleProblem(initialState));
if (!result.Any)
// fail
if (result.Count == 1 && result[0].IsNoOp)
// already at goal
foreach (var act in result)
{
if (act is MoveLeft) .....
}
But I see one inconvenience with my class design: in the concrete class implementation code I need to have a lot of casts from object. Is there any way to avoid that?
If I make the Problem class generic and inherit ConcreteProblem from Problem<Something> then I will not be able to use it via Problem...
Why don't you inject your GoalState too and make GameState also pluggable using the interface like below. In this way you can implement any operation you want to do on two States in the concrete class and Problem class just needs to call those functions. Also you can have different types of states too.
public abstract class Problem<T> where T:IGameState
{
protected Problem(T initialState)
{
InitialState = initialState;
}
public T InitialState { get; set; }
/// <summary>
/// Checks if the state is the goal state
/// </summary>
public abstract bool IsGoalState(T state);
}
public class EightPuzzleProblem<T> : Problem<T> where T : IGameState
{
private readonly T _goalState;
public EightPuzzleProblem(T initialState, T goalState)
: base(initialState)
{
_goalState = goalState;
}
public override bool IsGoalState(T state)
{
return _goalState.IsEqual(state);
}
}
public interface IGameState
{
bool IsEqual(IGameState state);
}
public class EightPuzzleGameState : IGameState
{
private readonly int[,] _goalState =
{
{0, 1, 2},
{3, 4, 5},
{6, 7, 8},
};
public bool IsEqual(IGameState state)
{
//Compare state with _goalState
}
}
You could actually make Problem generic with the generic param set to the actual state.
public abstract class Problem<T> where T : IState{
public abstract bool IsGoalState(T state);
}
Within your derived classes you can now simply derive from Problem passing the actual type of IState as generic parameter:
class MyDerived : Problem<MyState> {
public override bool IsGoalState(MyState state) { ... }
}
I am keep getting
System.Reflection.TargetInvocationException
and PresentationFramework.dll, additional info Exception has been thrown by the target of an invocation.
Can someone please help me out here?
Info:
Call Stack
PresentationFramework.dll!System.Windows.Markup.WpfXamlLoader.Load(System.Xaml.XamlReader xamlReader, System.Xaml.IXamlObjectWriterFactory writerFactory, bool skipJournaledProperties, object rootObject, System.Xaml.XamlObjectWriterSettings settings, System.Uri baseUri) Unknown
namespace PMD.Analysis.AnalysisViewModel
{
using PMD.Measurement.MeasurementModel;
using System.Windows.Data;
using PMD.Analysis.AnalysisModel;
using System;
using System.Collections.Generic;
using PMD.Measurement.MeasurementViewModel;
public class AnalysisViewModel : ViewModel
{
/// <summary>
/// New analysis command.
/// </summary>
private ICommand newAnalysis = null;
public PMD.Analysis.AnalysisViewModel.NewAnalysisViewModel m_NewAnalysisViewModel;
Measurement measurement = new Measurement();
private ICollectionView measurements = null;
/// <summary>
/// Measurement's search by title field.
/// </summary>
private string searchTitle;
/// <summary>
/// Measurement's search by title field.
/// </summary>
private string searchTester;
/// <summary>
/// Measurement's search by vehicle VIN field.
/// </summary>
private string searchVehicleVIN;
public MeasurementModel MeasurementModel
{
get;
set;
}
public enum SelectedState
{
// No Masurements.
Inactive,
// Masurements.
Active,
// Waiting for Masurements.
WaitingAnswer
};
public SelectedState CurrentSelectedState { get; set; }
public Analysis Analysis
{
get;
set;
}
public AnalysisViewModel()
{
Analysis = new Analysis();
measurements = new ListCollectionView(MeasurementModel.Measurements);
measurements.Filter = new Predicate<object>(SearchCallbackAnalysis);
}
~AnalysisViewModel()
{
}
/// <summary>
/// List of measurements that will be displayed in analysis view.
/// </summary>
public ICollectionView Measurements
{
get { return measurements; }
set { measurements = value; }
}
/// <summary>
/// Gets or sets new analysis command.
/// </summary>
public ICommand NewAnalysis
{
get
{
if (newAnalysis == null)
newAnalysis = new NewAnalysisCommand(this);
return newAnalysis;
}
}
public bool SearchCallbackAnalysis(object item)
{
bool isItemShowed = true;
if ((searchTitle != "") && (searchTitle != null))
isItemShowed &= (((Measurement)item).Title == searchTitle);
if ((searchVehicleVIN != "") && (searchVehicleVIN != null))
isItemShowed &= (((Measurement)item).Vehicle.VehicleVIN == searchVehicleVIN);
if ((SearchTester != "") && (SearchTester != null))
isItemShowed &= (((Measurement)item).Tester == SearchTester);
return isItemShowed;
}
/// <summary>
/// Gets or sets measurement's search by title field.
/// </summary>
public string SearchTitle
{
get
{
return searchTitle;
}
set
{
searchTitle = value;
Measurements.Refresh();
}
}
/// <summary>
/// Gets or sets measurement's search by tester name field.
/// </summary>
public string SearchTester
{
get
{
return searchTester;
}
set
{
searchTester = value;
Measurements.Refresh();
}
}
/// <summary>
/// Gets or sets measurement's search by vehicle VIN field.
/// </summary>
public string SearchVehicleVIN
{
get
{
return searchVehicleVIN;
}
set
{
searchVehicleVIN = value;
Measurements.Refresh();
}
}
}//end AnalysisViewModel
}//end namespace AnalysisViewModel
if i comment in constructor this line of code:
measurements.Filter = new Predicate<object>(SearchCallbackAnalysis);
Everything works fine but i need this line to search in the list.
Additional info:
xamlReader Cannot obtain value of local or argument 'xamlReader' as it is not available at this instruction pointer, possibly because it has been optimized away. System.Xaml.XamlReader
writerFactory Cannot obtain value of local or argument 'writerFactory' as it is not available at this instruction pointer, possibly because it has been optimized away. System.Xaml.IXamlObjectWriterFactory
skipJournaledProperties Cannot obtain value of local or argument 'skipJournaledProperties' as it is not available at this instruction pointer, possibly because it has been optimized away. bool
rootObject Cannot obtain value of local or argument 'rootObject' as it is not available at this instruction pointer, possibly because it has been optimized away. object
settings Cannot obtain value of local or argument 'settings' as it is not available at this instruction pointer, possibly because it has been optimized away. System.Xaml.XamlObjectWriterSettings
baseUri Cannot obtain value of local or argument 'baseUri' as it is not available at this instruction pointer, possibly because it has been optimized away. System.Uri
i have try:
public ICollectionView Measurements
{
get { return measurements; }
set { measurements = value;
measurements.Filter = new Predicate<object>(SearchCallbackAnalysis);
}
}
Now everything works fine. Thank you for try to help me.
As you can likely see from the title, I am about to ask something which has been asked many times before. But still, after reading all these other questions, I cannot find a decent solution to my problem.
I have a model class with basic validation:
partial class Player : IDataErrorInfo
{
public bool CanSave { get; set; }
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "Firstname")
{
if (String.IsNullOrWhiteSpace(Firstname))
{
result = "Geef een voornaam in";
}
}
if (columnName == "Lastname")
{
if (String.IsNullOrWhiteSpace(Lastname))
{
result = "Geef een familienaam in";
}
}
if (columnName == "Email")
{
try
{
MailAddress email = new MailAddress(Email);
}
catch (FormatException)
{
result = "Geef een geldig e-mailadres in";
}
}
if (columnName == "Birthdate")
{
if (Birthdate.Value.Date >= DateTime.Now.Date)
{
result = "Geef een geldige geboortedatum in";
}
}
CanSave = true; // this line is wrong
return result;
}
}
public string Error { get { throw new NotImplementedException();} }
}
This validation is done everytime the property changes (so everytime the user types a character in the textbox):
<TextBox Text="{Binding CurrentPlayer.Firstname, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="137" IsEnabled="{Binding Editing}" Grid.Row="1"/>
This works perfect. The validation occurs (the PropertyChanged code for the binding is done in the VM on the CurrentPlayer property, which is an object of Player).
What I would like to do now is disable the save button when the validation fails.
First of all, the easiest solutions seems to be found in this thread:
Enable Disable save button during Validation using IDataErrorInfo
If I want to follow the accepted solution, I'd have to write my
validation code twice, as I cannot simply use the indexer. Writing
double code is absolutely not what I want, so that's not a solution
to my problem.
The second answer on that thread sounded very promising as first,
but the problem is that I have multiple fields that have to be
validated. That way, everything relies on the last checked property
(so if that field is filled in correctly, CanSave will be true, even
though there are other fields which are still invalid).
One more solution I've found is using an ErrorCount property. But as I'm validating at each property change (and so at each typed character), this isn't possible too - how could I know when to increase/decrease the ErrorCount?
What would be the best way to solve this problem?
Thanks
This article http://www.asp.net/mvc/tutorials/older-versions/models-%28data%29/validating-with-the-idataerrorinfo-interface-cs moves the individual validation into the properties:
public partial class Player : IDataErrorInfo
{
Dictionary<string, string> _errorInfo;
public Player()
{
_errorInfo = new Dictionary<string, string>();
}
public bool CanSave { get { return _errorInfo.Count == 0; }
public string this[string columnName]
{
get
{
return _errorInfo.ContainsKey(columnName) ? _errorInfo[columnName] : null;
}
}
public string FirstName
{
get { return _firstName;}
set
{
if (String.IsNullOrWhiteSpace(value))
_errorInfo.AddOrUpdate("FirstName", "Geef een voornaam in");
else
{
_errorInfo.Remove("FirstName");
_firstName = value;
}
}
}
}
(you would have to handle the Dictionary AddOrUpdate extension method). This is similar to your error count idea.
I've implemented the map approach shown in my comment above, in C# this is called a Dictionary in which I am using anonymous methods to do the validation:
partial class Player : IDataErrorInfo
{
private delegate string Validation(string value);
private Dictionary<string, Validation> columnValidations;
public List<string> Errors;
public Player()
{
columnValidations = new Dictionary<string, Validation>();
columnValidations["Firstname"] = delegate (string value) {
return String.IsNullOrWhiteSpace(Firstname) ? "Geef een voornaam in" : null;
}; // Add the others...
errors = new List<string>();
}
public bool CanSave { get { return Errors.Count == 0; } }
public string this[string columnName]
{
get { return this.GetProperty(columnName); }
set
{
var error = columnValidations[columnName](value);
if (String.IsNullOrWhiteSpace(error))
errors.Add(error);
else
this.SetProperty(columnName, value);
}
}
}
This approach works with Data Annotations. You can also bind the "IsValid" property to a Save button to enable/disable.
public abstract class ObservableBase : INotifyPropertyChanged, IDataErrorInfo
{
#region Members
private readonly Dictionary<string, string> errors = new Dictionary<string, string>();
#endregion
#region Events
/// <summary>
/// Property Changed Event
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Protected Methods
/// <summary>
/// Get the string name for the property
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression"></param>
/// <returns></returns>
protected string GetPropertyName<T>(Expression<Func<T>> expression)
{
var memberExpression = (MemberExpression) expression.Body;
return memberExpression.Member.Name;
}
/// <summary>
/// Notify Property Changed (Shorted method name)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression"></param>
protected virtual void Notify<T>(Expression<Func<T>> expression)
{
string propertyName = this.GetPropertyName(expression);
PropertyChangedEventHandler handler = this.PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Called when [property changed].
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expression">The expression.</param>
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> expression)
{
string propertyName = this.GetPropertyName(expression);
PropertyChangedEventHandler handler = this.PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region Properties
/// <summary>
/// Gets an error message indicating what is wrong with this object.
/// </summary>
public string Error => null;
/// <summary>
/// Returns true if ... is valid.
/// </summary>
/// <value>
/// <c>true</c> if this instance is valid; otherwise, <c>false</c>.
/// </value>
public bool IsValid => this.errors.Count == 0;
#endregion
#region Indexer
/// <summary>
/// Gets the <see cref="System.String"/> with the specified column name.
/// </summary>
/// <value>
/// The <see cref="System.String"/>.
/// </value>
/// <param name="columnName">Name of the column.</param>
/// <returns></returns>
public string this[string columnName]
{
get
{
var validationResults = new List<ValidationResult>();
string error = null;
if (Validator.TryValidateProperty(GetType().GetProperty(columnName).GetValue(this), new ValidationContext(this) { MemberName = columnName }, validationResults))
{
this.errors.Remove(columnName);
}
else
{
error = validationResults.First().ErrorMessage;
if (this.errors.ContainsKey(columnName))
{
this.errors[columnName] = error;
}
else
{
this.errors.Add(columnName, error);
}
}
this.OnPropertyChanged(() => this.IsValid);
return error;
}
}
#endregion
}
Consider the sample code below consisting of a Class Library design and an executable Program using the library.
namespace AppLib
{
/// <summary>
/// Entry point for library. Stage manages all the actors in the logic.
/// </summary>
class StageApp
{
/// <summary>
/// Setting that is looked up by different actors
/// </summary>
public int SharedSetting { get; set; }
/// <summary>
/// Stage managing actors with app logic
/// </summary>
public IEnumerable<Actor> Actors { get { return m_actors.Where(x => x.Execute() > 40).ToArray(); } }
private List<Actor> m_actors = new List<Actor>();
}
/// <summary>
/// An object on the stage. Refers to stage (shared)settings and execute depending on the settings.
/// Hence actor should have reference to stage
/// </summary>
class Actor
{
private StageApp m_StageApp;
private int m_Property;
/// <summary>
/// An actor that needs to refer to stage to know what behavior to execute
/// </summary>
/// <param name="stage"></param>
public Actor(StageApp stage)
{
m_StageApp = stage;
m_Property = new Random().Next();
}
/// <summary>
/// Execute according to stage settings
/// </summary>
/// <returns></returns>
public int Execute()
{
return m_StageApp.SharedSetting * m_Property;
}
}
}
namespace AppExe
{
using AppLib;
class Program
{
static void Main(string[] args)
{
StageApp app = new StageApp();
app.SharedSetting = 5;
// Question: How to add actor to stage?
foreach (var actor in app.Actors)
Console.WriteLine(actor.Execute());
}
}
}
Question
Stage and Actor have circular dependency and seems bad to me.
For example, how should we add actors to stage?
If I let user to create new Actor() themselves,
then they must keep on supplying the Stage.
If I give Actor() an internal constructor and make Stage a factory,
then I lose some of the flexibility for users to do making inherited Actors.
If I make Stage a singleton, then I can only have one set of SharedSetting.
In case the user wants more than one Stage in his AppExe, then it cannot be done.
Is there anyway to redesign the architecture so as to avoid the problems above?
If your functionality is not limited by sharing the StageApp settings between actors, but also will be some other logic. For example when you need to know parent StageApp from Actor and vice versa. I preffer to implement it in this way:
namespace AppLib
{
/// <summary>
/// Entry point for library. Stage manages all the actors in the logic.
/// </summary>
class StageApp
{
/// <summary>
/// Setting that is looked up by different actors
/// </summary>
public int SharedSetting { get; set; }
/// <summary>
/// Stage managing actors with app logic
/// </summary>
public IEnumerable<Actor> Actors { get { return m_actors.Where(x => x.Execute() > 40).ToArray(); } }
private List<Actor> m_actors = new List<Actor>();
public int TotalActorsCount
{
get
{
return m_actors.Count;
}
}
public void AddActor(Actor actor)
{
if (actor == null)
throw new ArgumentNullException("actor");
if (m_actors.Contains(actor))
return; // or throw an exception
m_actors.Add(actor);
if (actor.Stage != this)
{
actor.Stage = this;
}
}
// we are hiding this method, to avoid because we can change Stage only to another non null value
// so calling this method directly is not allowed
internal void RemoveActor(Actor actor)
{
if (actor == null)
throw new ArgumentNullException("actor");
if (!m_actors.Contains(actor))
return; // or throuw exception
m_actors.Remove(actor);
}
}
/// <summary>
/// An object on the stage. Refers to stage (shared)settings and execute depending on the settings.
/// Hence actor should have reference to stage
/// </summary>
class Actor
{
private StageApp m_StageApp;
private int m_Property;
public StageApp Stage
{
get
{
return m_StageApp;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
if (m_StageApp != value)
{
if (m_StageApp != null) // not a call from ctor
{
m_StageApp.RemoveActor(this);
}
m_StageApp = value;
m_StageApp.AddActor(this);
}
}
}
/// <summary>
/// An actor that needs to refer to stage to know what behavior to execute
/// </summary>
/// <param name="stage"></param>
public Actor(StageApp stage)
{
Stage = stage;
m_Property = new Random().Next();
}
/// <summary>
/// Execute according to stage settings
/// </summary>
/// <returns></returns>
public int Execute()
{
return m_StageApp.SharedSetting * m_Property;
}
}
}
namespace AppExe
{
using AppLib;
class Program
{
static void Main(string[] args)
{
StageApp app = new StageApp();
app.SharedSetting = 5;
StageApp anotherApp = new StageApp();
anotherApp.SharedSetting = 6;
// actor is added to the stage automatically after instantiation
Actor a1 = new Actor(app);
Actor a2 = new Actor(app);
Actor a3 = new Actor(anotherApp);
Console.WriteLine("Actors in anotherApp before moving actor:");
Console.WriteLine(anotherApp.TotalActorsCount);
// or by calling method from StageApp class
anotherApp.AddActor(a1);
Console.WriteLine("Actors in anotherApp after calling method (should be 2):");
Console.WriteLine(anotherApp.TotalActorsCount);
// or by setting Stage through property
a2.Stage = anotherApp;
Console.WriteLine("Actors in anotherApp after setting property of Actor instance (should be 3):");
Console.WriteLine(anotherApp.TotalActorsCount);
Console.WriteLine("Actors count in app (should be empty):");
Console.WriteLine(app.TotalActorsCount);
}
}
}
It allows to you to manipulate with object relationships transparently, but requires a little bit mor code to implement.
How about adding a new class "ActorRole" that defines the behaviour of the actor in each Stage. It lets you decouple Actor and Stage from each other, so you can instantiate both independently (through a factory for example) and then combine them creating ActorRole objects that configure your stages. This combinations can be made using a Builder pattern if it is needed.
If you need to dynamically change your actor behaviour, you can use a Strategy pattern based on the ActorRole class, so depending on the Stage, you can assign to the actor different concrete implementations of its behaviour.
I would solve it by using Func instead of passing in the Stage to the Actor. Like this:
namespace AppExe
{
using AppLib;
class Program
{
static void Main(string[] args)
{
StageApp app = new StageApp();
app.CreateActor();
app.SharedSetting = 5;
foreach (var actor in app.Actors)
Console.WriteLine(actor.Execute());
}
}
}
namespace AppLib
{
class StageApp
{
public int SharedSetting { get; set; }
public IEnumerable<Actor> Actors { get { return m_actors.Where(x => x.Execute() > 40).ToArray(); } }
private List<Actor> m_actors = new List<Actor>();
public void CreateActor()
{
m_actors.Add(new Actor(Executed));
}
private int Executed(int arg)
{
return SharedSetting * arg;
}
}
class Actor
{
private int m_Property;
private Func<int, int> m_executed;
public Actor(Func<int, int> executed)
{
m_executed = executed;
m_Property = new Random().Next();
}
public int Execute()
{
return m_executed(m_Property);
}
}
}
I totally agree with you that circular references is not fun :).
You could also solve this using events, but I like passing functions like callback.