Josh
Josh

Reputation: 8477

Interface passing in a Setter compile error

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

Answers (2)

nicodemus13
nicodemus13

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

Maku
Maku

Reputation: 1558

Have you tried this:

SetField(ref _customerForOwner, 
    (Customer)value, () => (Customer)CustomerForOwner);

since CustomerOfOwner is of interface type.

Upvotes: 1

Related Questions