Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Internal property setters in C#

I'm trying to figure out a good way to approach this. I have a Customer class which implements the ICustomer interface. This interface has a number of properties in it:

public interface ICustomer
{

   string FirstName {get; set;}
   string LastName  {get; set;}
}

I only want certain classes to have the ability to set those properties however; namely, those classes in the project. So I thought about making the setter internal:

public class Customer : ICustomer
{

   string FirstName {get; internal set;}
   string LastName  {get; internal set;}
}

I'd like to mark that setter as internal in the interface however, so there's no chance someone implements ICustomer and someone outside the assembly modifies those properties. Is there a good way to do this?

like image 717
larryq Avatar asked Nov 28 '12 20:11

larryq


People also ask

What does internal set mean?

In mathematical logic, in particular in model theory and nonstandard analysis, an internal set is a set that is a member of a model.

What does internal set mean C#?

If you have a property with an internal set accessor (and public get accessor) it means that code within the assembly can read (get) and write (set) the property, but other code can only read it.

What is the difference between internal and private?

Private: - Private members are only accessible within the own type (Own class). Internal: - Internal member are accessible only within the assembly by inheritance (its derived type) or by instance of class.

What are INIT only setters?

init (C# Reference) An init-only setter assigns a value to the property or the indexer element only during object construction. This enforces immutability, so that once the object is initialized, it can't be changed again. For more information and examples, see Properties, Auto-Implemented Properties, and Indexers.


2 Answers

The properties in the interface should be read only. It's acceptable for the concrete class that implements the interface to have a setter even if none is defined in the interface.

public interface ICustomer
{
   string FirstName { get; }
   string LastName  { get; }
}

public class Customer : ICustomer
{
   public string FirstName { get; internal set; }
   public string LastName  { get; internal set; }
}

If it's really important that the setter be exposed through an interface, rather than having the interface being entirely read-only, you can use something like this:

public interface IReadCustomer
{
    string FirstName { get; }
    string LastName { get; }
}

internal interface IWriteCustomer
{
    string FirstName { set; }
    string LastName { set; }
}

internal interface IReadWriteCustomer : IReadCustomer, IWriteCustomer
{ }

public class Customer : IReadWriteCustomer
{
    private string _firstName;
    private string _lastName;

    public string FirstName
    {
        get { return _firstName; }
        internal set { _firstName = value; }
    }
    public string LastName
    {
        get { return _lastName; }
        internal set { _lastName = value; }
    }

    string IReadCustomer.FirstName
    {
        get { return FirstName; }
    }

    string IReadCustomer.LastName
    {
        get { return LastName; }
    }

    string IWriteCustomer.FirstName
    {
        set { FirstName = value; }
    }

    string IWriteCustomer.LastName
    {
        set { LastName = value; }
    }
}
like image 127
Servy Avatar answered Sep 20 '22 12:09

Servy


I'd like to mark that setter as internal in the interface however, so there's no chance someone implements ICustomer and someone outside the assembly modifies those properties. Is there a good way to do this?

No. Property members are always public, unfortunately. Additionally, messing around with access levels on properties where part of it is specified on the interface gets painful, IIRC. What you can do is this:

public interface ICustomer
{
    string FirstName { get; }
    string SecondName { get; }
}

internal interface ICustomerWithSetMethods : ICustomer
{
    void SetFirstName(string name);
    void SetLastName(string name);
}

public class Customer : ICustomerWithSetMethods

Then from the outside it'll look like Customer only implements ICustomer, but from inside your code will see that it implements ICustomerWithSetMethods.

Unfortunately that doesn't play nicely if your API needs to declare any public methods where you'd really like to just declare a return type of ICustomer, but you'll actually know that it's always ICustomerWithSetMethods.

Assuming you still want to allow multiple implementations, you could potentially go for an abstract class instead:

public abstract class CustomerBase
{
    public abstract string FirstName { get; }
    public abstract string LastName { get; }

    internal abstract void SetFirstName(string name);
    internal abstract void SetLastName(string name);
}

Now we have the slight oddity that no-one outside the assembly can extend your CustomerBase, because there are abstract methods they'd have to override that they can't even see - but it does mean you can use CustomerBase everywhere in your API.

This is the approach we took in Noda Time for calendar systems in the end - I blogged about it when I first came up with the plan. I generally prefer interfaces to abstract classes, but the benefit here was significant.

like image 41
Jon Skeet Avatar answered Sep 19 '22 12:09

Jon Skeet