Search code examples
c#c#-8.0default-interface-member

Default interface methods and default values for Auto Properties


Given that an auto property compiles to a get_method, a set_method and a private variable and since C# 8 is introducing default interface methods

Can properties in Interfaces have default implementations?

Especially a get only property?


Solution

  • No and Yes. An interface can't have state but you can define a property with {get;set;}.

    Auto properties aren't a special type of property. They are a convenience feature that generates the code needed to store property values in a backing field.

    You can specify a default implementation for properties, both for getters and setters. You can try the following code in Sharplab.io

    public interface IDimensions
    {
        int Height {get;set;}
        int Width {get;set;}
        int Depth {get;set;}
    
        int Weight { get=>0; set{} }
        int Density { get=> Weight==0?0:Height*Width*Depth/Weight ; }
    }
    
    public class Box:IDimensions
    {
        public int Height{get;set;}
        public int Width{get;set;}
        public int Depth {get;set;}
    }
    

    Versioning

    This demonstrates the versioning scenario. Box implemented a version of IDimensions that only included Height, Width.

    Weight was added later with a default implementation that returns 0 and ignores writes. Density was added with a default implementation that returns the volume/density of a box, or 0 if there's no valid weight. Box didn't have to change even though the interfade did. Even the code generated by the compiler doesn't show any changes to the Box class.

    Classes can replace the default implementations with their own. Nothing prevents Box from adding int Weight {get;set;} in the future.

    The default implementations are available only through the interface :

    IDimensions box=new Box();
    Console.WriteLine(box.Density);
    

    Traits

    The other scenario handled by default implementations is traits.

    Let's assume I want to add the IDensity trait to any type of item. In most cases, I'd only need the volume and weight of an item to calculate its density :

    public interface IDensity
    {
        int Density 
        { 
            get 
            {
                var weight=getWeight();
                if (weight==0) return 0;
                return getVolume()/weight;
            }
        }
    
        abstract int getWeight();
        abstract int getVolume();
    }
    

    This trait will return a simplistic density calculation and force the type it's applied to to implement the int getWeight() and an int getHeight() methods:

    public class Crate:IDensity
    {
        //Dummy numbers
        public int getWeight()=>55;
        public int getVolume()=>100;
    }
    
    public class C {
        public void M() {
            IDensity box=new Cake();
            Console.WriteLine(box.Density);
        }
    }
    

    Another container could override that implementation with its own. Perhaps the container has a complex shape :

    public class WeddingCake:IDensity
    {
        public int getWeight()=>5;
        public int getVolume()=>getWeight()/Density;
        public int Density=>2;
    }
    

    The Sharplab.io link for this example is here