Reputation: 8477
I have a property called CustomerForOwner on a class called Owner. I want a read only version of the Owner class so I created a wrapper class called OwnerReadOnly. The issue I've run into is when I have reference type properties. To create the ReadOnly version of that object I used an Interface so that both Owner and OwnerReadOnly could have a property called CustomerForOwner (ICustomer). OwnerReadOnly.CustomerForOwner would return CustomerReadOnly and Owner.CustomerForOwner would return Customer.
Simplified version of classes:
public class Owner : ProjectBase<Owner>, IOwner
{
private Customer _customerForOwner;
private string _ownerName
public virtual ICustomer CustomerForOwner
{
get { return _customerForOwner; }
set
{
SetField(ref _customerForOwner, value, () => CustomerForOwner);
value.PropertyChanged += this.OnItemPropertyChanged;
}
}
public virtual string OwnerName
{
get { return _ownerName; }
set { SetField(ref _ownerName, value, () => OwnerName); }
}
public Owner(DateTime created, string createdBy) :
base(created, createdBy) { }
}
public class OwnerReadOnly : Owner
{
public override ICustomer CustomerForOwner
{
get { return (CustomerReadOnly)base.CustomerForOwner; }
}
public override string OwnerName
{
get { return base.OwnerName; }
}
public OwnerReadOnly(DateTime created, string createdBy) :
base(created, createdBy)
{
throw new Exception("Object is ReadOnly, cannot create a new instance");
}
}
Base Class:
public abstract class ProjectBase<T> : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isActive;
public bool IsActive
{
get { return _isActive; }
set { SetField(ref _isActive, value,() => IsActive ); }
}
public DateTime Created { get; private set; }
public string CreatedBy { get; private set; }
public DateTime? LastUpdated { get; protected set; }
public string LastUpdatedBy { get; protected set; }
public bool IsDirty { get; protected set; }
private ProjectBase() { }
protected ProjectBase(DateTime created, string createdBy)
{
IsActive = true;
Created = created;
CreatedBy = createdBy;
LastUpdated = created;
LastUpdatedBy = createdBy;
IsDirty = false;
}
public abstract void Clone();
public abstract void Create();
public abstract void Update(DateTime lastUpdated, string lastUpdatedBy);
protected abstract void Update();
public abstract void Delete();
protected bool SetField<TField>(ref TField field, TField value, Expression<Func<TField>> selectorExpression)
{
bool returnValue = false;
if (EqualityComparer<TField>.Default.Equals(field, value))
returnValue = false;
else
{
field = value;
IsDirty = true;
OnPropertyChanged(selectorExpression);
returnValue = true;
}
return returnValue;
}
protected virtual void OnPropertyChanged<TParam>(Expression<Func<TParam>> selectorExpression)
{
MemberExpression body;
if (selectorExpression == null)
throw new ArgumentNullException("selectorExpression");
body = selectorExpression.Body as MemberExpression;
if (body == null)
throw new ArgumentException("The body must be a member expression");
OnPropertyChanged(body.Member.Name);
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
IsDirty = true;
}
protected void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
IsDirty = true;
}
The problem I've run into is calling SetField with the Owner.CustomerForOwner property line:
SetField(ref _customerForOwner, value, () => CustomerForOwner);
I receive the following compile error: The type arguments for method 'ProjectBase.SetField(ref TField, TField, System.Linq.Expressions.Expression>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
How can I pass pass ICustomer as a Customer? I tied changing it to:
SetField(ref _customerForOwner, (Customer)value, () => CustomerForOwner);
but same error. I also tried setting value to a new Customer on the above the line in the Setter but the same compile error was returned.
Upvotes: 1
Views: 157
Reputation: 2298
The method
SetField(ref _customerForOwner, value, () => CustomerForOwner);
takes type a single type-parameter (TField
), however
_customerForOwner
is a Customer
, value
is an ICustomer
and CustomerForOwner
is an ICustomer
, so the compiler cannot infer the types, because, when it tries to infer that _customerForOwner
is an ICustomer
it must cast the concrete type to an interface.
This is not allowed because a C# language rule is that:
A ref or out argument must be an assignable variable
The cast results in non-assigned variable.
If you do this:
public virtual ICustomer CustomerForOwner
{
get { return _customerForOwner; }
set
{
var customerForOwner = (ICustomer)_customerForOwner;
SetField(ref customerForOwner, value, () => CustomerForOwner);
_customerForOwner = customerForOwner as Customer;
value.PropertyChanged += this.OnItemPropertyChanged;
}
}
Then the code will compile. Whether it will work and do what you want, I'm not sure. The code looks a bit odd to me. :) For instance, SetField
returns a boolean, which are you aren't using.
Upvotes: 1
Reputation: 1558
Have you tried this:
SetField(ref _customerForOwner,
(Customer)value, () => (Customer)CustomerForOwner);
since CustomerOfOwner
is of interface type.
Upvotes: 1