Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Idempotent interface method in c#

Tags:

c#

oop

interface

I am trying to create an interface that returns a modified copy of an instance that implements it and does not modify the original instance.

public interface ICensoreable<T> {
    T GetCensored();
}

And the object that implements

public class User:ICensoreable<User> {
    public User(User copyFrom) {
        this.name = copyFrom.name;
        this.password = copyFrom.password;
    }

    public string name;
    public string password;

    public User GetCensored() {
        User result = new User(this);
        result.password = null;

        return result;
    }
}

Is there any way to enforce on an interface that GetCensored will not modify User (or T) instance?

like image 343
Daniel Avatar asked Apr 30 '19 12:04

Daniel


1 Answers

With the recent release of C# 8.0, you can now define default method implementations in your interface. Therefore, you will be able to enforce that by default the GetCensored() method does not modify the original instance. By marking the default implementation as sealed, the type that implements the interface is prohibited from explicitly reimplementing the method. Since the method is only implemented in the interface, you need to cast the object to ICensoreable<T> before calling the GetCensored() method.

This is the implementation that I used:

public interface ICensoreable<T>
{
    sealed ICensoreable<T> GetCensored()
    {
        var result = Clone();
        result.CensorInformation();
        return result;
    }

    ICensoreable<T> Clone();
    void CensorInformation();
}

public class User : ICensoreable<User>
{
    public User(User other)
    {
        name = other.name;
        password = other.password;
    }

    public string name;
    public string password;

    public void CensorInformation()
    {
        password = null;
    }

    public User Clone() => new User(this);
    ICensoreable<User> ICensoreable<User>.Clone() => Clone();
}

Using the GetCensored() method:

var user = new User();
var censored = ((ICensoreable<User>)user).GetCensored();

NOTE: As of the time of writing, this implementation will throw a NullReferenceException at the first line of the GetCensored() method (using C# 8.0 on VS 16.4.0 Preview 2.0). I personally assume this is a bug, since removing the sealed keyword will not cause any issues whatsoever with the exact same code. Furthermore, I tried another implementation which involved initializing a new object of type T, which also crashed. The line was var result = new T(); and it threw a NullReferenceException. This is fixed in VS 16.5.4 onwards and the code runs correctly.

Theoretically, and as per the proposal, the sealed keyword simply prevents types from overriding the interface method and only keeping the default implementation.

EDIT 1: Currently at VS 16.4.0 Preview 5.0, the exception being thrown is AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'. After investigating a bit I realized that the bug has been known for a few weeks with a relevant GitHub issue. In accordance with somebody this is fixed in 16.5.0 Preview 1.0 onwards, after a follow-up fix.

EDIT 2: Currently at VS 16.5.0 Preview 1.0, the exception is still the same; there was no fix for this issue being released yet.

FINAL EDIT: From VS 16.5.4 onwards (I hope), this code works just fine. No exception is being thrown.

like image 159
Rekkon Avatar answered Nov 08 '22 22:11

Rekkon