Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Set a read only property defined in a interface within a concrete class

I have an interface with a read only property

public interface IPerson
{
    string Name { get; }
}

and a concrete class...

public class Person : IPerson
{

    public Person()
    {
        Name = "Person";
    }

    public string Name
    {
        get
        {
            return Name;
        }
    }
}

I want Name to be read only externally to this class, but how do I set it from within the concrete class?

Error: Person.Name cannot be assigned to.

How do I set the value of this property from within Person class?

like image 495
MikeJ Avatar asked Jul 22 '16 07:07

MikeJ


6 Answers

This doesn't have anything to do with the interface, you're just declaring the property incorrectly. In C# 6, you can create a read-only property like this:

public class Person : IPerson
{
    public Person()
    {
        Name = "Person";
    }

    public string Name { get; }
}

In earlier versions, you can use a read-only backing field which you can set:

public class Person : IPerson
{
    private readonly string _name;

    public Person()
    {
        _name = "Person";
    }

    public string Name
    {
        get { return _name; }
    }
}

Note that the interface only requires the property has a getter, the implementation doesn't have to be read-only. You could add a setter if you had reason to modify the value:

public class Person : IPerson
{
    public Person()
    {
        Name = "Person";
    }

    public string Name { get; set; }
}

The setter could be private if you only needed to be able to change the value from within the class.

like image 124
Charles Mager Avatar answered Oct 17 '22 10:10

Charles Mager


You can use a private property to hold the value.

public class Person : IPerson
{
    private string _name;
    public Person()
    {
        _name = "Person";
    }

    public string Name
    {
        get
        {
            return _name;
        }
    }
}
like image 23
Edward N Avatar answered Oct 17 '22 11:10

Edward N


Right now, you're trying to read the property by reading the property. Needless to say, this will result in an endless loop. Instead, you either need to use full-blown auto-properties, or a manual backing field.

public class Person : IPerson
{
    public Person()
    {
        Name = "Person";
    }

    public string Name { get; private set; }
}
like image 25
Luaan Avatar answered Oct 17 '22 11:10

Luaan


You simply have a private setter (pre c#6):

public class Person : IPerson
{

    public Person()
    {
        Name = "Person";
    }

    public string Name { get; private set; }

}
like image 43
Callum Linington Avatar answered Oct 17 '22 09:10

Callum Linington


Try using accessors:

private string _name;

public string Name
{
    get
    {
        return _name;
    }
}

You can then set the value of _name inside any method or the constructor.

like image 3
KaeL Avatar answered Oct 17 '22 10:10

KaeL


I want Name to be read only externally to this class, but how do I set it from within the concrete class

First of all, let's realize that: an interface only describes a set of public requirements; it does not prevent us from implementing other public members, nor does it limit our ability to create private members.

Therefore, to make a property writeable from inside the class but read-only elsewhere, we can declare the set method with private scope:

public class Person : IPerson
{
    // Name is read-only outside the class, but can be set internally
    public string Name { get; private set; } = "DefaultName";

    public Person() { }

    public Person(string name)
    {
        // Potentially do some validation before setting the name
        if (!IsValidName(name)) 
            throw new ArgumentException("Name cannot be null, empty, or whitespace.");

        Name = name;
    }

    private bool IsValidName(string name)
    {
        return !string.IsNullOrWhitespace(name);
    }

    // Continued below...

And if we want to, we can give access to the private setter through a method. This is often done in order to make the renaming of a person very intentional (for example, it prevents accidental assignments when a comparison was intended). The client will get a design-time error from the compiler if they try person.Name = "Foo"; but they can write person.RenameAs("Foo");

    public void RenameAs(string newName)
    {
        // Potentially do some validation before setting the name
        if (IsValidName(newName)) Name = newName;
    }
}
like image 1
Rufus L Avatar answered Oct 17 '22 11:10

Rufus L