Search code examples
c#recordinterface-implementation

C# record and implement interface with nullable


I have the following interface:

public interface IEmailModel
{
    EmailAddressDto? Receiver { get; set; }
}

now I want to implement it for records. Try this:

public record EmailModelSendConfirmationToUserAndSuperadmin(EmailAddressDto? Receiver = null) : IEmailModel;

it's ok, no errors

try this:

public record EmailModelSendConfirmationEmailCodeToUser(string link, EmailAddressDto? Receiver = null) : IEmailModel;

here I get

'EmailModelSendConfirmationEmailCodeToUser' does not implement interface member 'IEmailModel.Receiver.set'. 'EmailModelSendConfirmationEmailCodeToUser.Receiver.init' cannot implement 'IEmailModel.Receiver.set'.

I really don't understand why should be implemented set if parameter can be nullable, but ok

I change my interface to:

public interface IEmailModel
{
    EmailAddressDto? Receiver { get; init; }
}

Class EmailModelSendConfirmationEmailCodeToUser no error, but class EmailModelSendConfirmationToUserAndSuperadmin has:

'EmailModelSendConfirmationToUserAndSuperadmin' does not implement interface member 'IEmailModel.Receiver.init'. 'EmailModelSendConfirmationToUserAndSuperadmin.Receiver.set' cannot implement 'IEmailModel.Receiver.init'.

why so and how to implement this interface for records?


Solution

  • From Positional syntax for property definition section of the docs:

    When you use the positional syntax for property definition, the compiler creates:

    A public autoimplemented property for each positional parameter provided in the record declaration.

    • For record types and readonly record struct types: An init-only property.
    • For record struct types: A read-write property.

    So either remove set or change it to init:

    public interface IEmailModel
    {
        EmailAddressDto? Receiver { get; init; } // or  just { get; }
    }
    

    Alternatively use "simple" property:

    public record EmailModelSendConfirmationToUserAndSuperadmin() : IEmailModel
    {
        public EmailAddressDto? Receiver { get; set; }
    }
    

    You can check that positional parameters for record types are turned by the compiler into init properties in decomplication @sharplab, note that both classes work for me.