63

I know the feature doesn't exist in C#, but PHP recently added a feature called Traits which I thought was a bit silly at first until I started thinking about it.

Say I have a base class called Client. Client has a single property called Name.

Now I'm developing a re-usable application that will be used by many different customers. All customers agree that a client should have a name, hence it being in the base-class.

Now Customer A comes along and says he also need to track the client's Weight. Customer B doesn't need the Weight, but he wants to track Height. Customer C wants to track both Weight and Height.

With traits, we could make the both the Weight and the Height features traits:

class ClientA extends Client use TClientWeight
class ClientB extends Client use TClientHeight
class ClientC extends Client use TClientWeight, TClientHeight

Now I can meet all my customers' needs without adding any extra fluff to the class. If my customer comes back later and says "Oh, I really like that feature, can I have it too?", I just update the class definition to include the extra trait.

How would you accomplish this in C#?

Interfaces don't work here because I want concrete definitions for the properties and any associated methods, and I don't want to re-implement them for each version of the class.

(By "customer", I mean a literal person who has employed me as a developer, whereas by "client" I'm referring a programming class; each of my customers has clients that they want to record information about)

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • 3
    Well, you can quite perfectly simulate traits in C# by using marker interfaces and extension methods. – Lucero May 23 '12 at 23:27
  • 2
    @Lucero Those are not traits and lack the ability to add new members (among other things). Nevertheless, extension methods are nifty. –  May 23 '12 at 23:29
  • 3
    @Lucero: That would work for adding extra methods, but what if I want to store additional data on the client object as well? – mpen May 23 '12 at 23:29
  • 1
    @Mark, then you need to have some ability to dynamically store data on arbitrary objects, which is not a feature of the runtime. I'll add some info on my answer on that regard. – Lucero May 23 '12 at 23:45
  • @Lucero And is an issue that is solved by Traits... ;-) –  May 23 '12 at 23:49
  • @pst, sure, but in the end they don't add multiple inheritance either. The challenge therefore is mostly to efficiently leverage the tools and have the compiler give us a nice syntax - which is why I called it simulating. With my approach and an object that also supports the DLR (see my edit) you do get far in regards of trait simulation as long as you have control over the base classes; adding them to existing classes where the inheritance chain cannot be altered is however a different topic. – Lucero May 24 '12 at 00:19
  • @Lucero "flattened" Traits such as those in Scala and Squeak are explicitly designed (at least in Odersky's view ;-) *not* to open up the MI can'o'worms. (An added benefit is they can run without an altered runtime environment.) Since the syntax of a language is tied to the repeated usability of certain constructs it must not be overlooked. Scala has a working version with Traits in the CLR; there is no reason to require the DLR as it's "all" or "only" in the language at this point (perhaps C# 6.0?). The usage of Extension methods shown is interesting, but ultimately a different tool. –  May 24 '12 at 02:57
  • @Lucero That is, "flattened" Traits explicitly do *not* alter the inheritance chain but they *do* alter the members present in the type assuming the traits. This can be implemented by-hand with proxy-methods to implementations (as well as by-hand declarations of data members) only, the by-hand approach is considered composition (it's just tedious to do it all manually). Extension methods work well in the precise opposite scenario; when the type itself cannot be altered. –  May 24 '12 at 03:03
  • nroles gives you traits (kind of) with a c# post compiler: https://code.google.com/p/nroles/ – Matthew Lock Jul 09 '13 at 01:00
  • 6
    Traits are coming to C# in the form of *default interface methods*. See [this proposal](https://github.com/dotnet/csharplang/blob/master/proposals/default-interface-methods.md) and [the corresponding issue](https://github.com/dotnet/csharplang/issues/52). (I'd post an answer but I don't know enough about it yet to post anything meaningful.) – user247702 Aug 25 '17 at 13:44

8 Answers8

64

You can get the syntax by using marker interfaces and extension methods.

Prerequisite: the interfaces need to define the contract which is later used by the extension method. Basically the interface defines the contract for being able to "implement" a trait; ideally the class where you add the interface should already have all members of the interface present so that no additional implementation is required.

public class Client {
  public double Weight { get; }

  public double Height { get; }
}

public interface TClientWeight {
  double Weight { get; }
}

public interface TClientHeight {
  double Height { get; }
}

public class ClientA: Client, TClientWeight { }

public class ClientB: Client, TClientHeight { }

public class ClientC: Client, TClientWeight, TClientHeight { }

public static class TClientWeightMethods {
  public static bool IsHeavierThan(this TClientWeight client, double weight) {
    return client.Weight > weight;
  }
  // add more methods as you see fit
}

public static class TClientHeightMethods {
  public static bool IsTallerThan(this TClientHeight client, double height) {
    return client.Height > height;
  }
  // add more methods as you see fit
}

Use like this:

var ca = new ClientA();
ca.IsHeavierThan(10); // OK
ca.IsTallerThan(10); // compiler error

Edit: The question was raised how additional data could be stored. This can also be addressed by doing some extra coding:

public interface IDynamicObject {
  bool TryGetAttribute(string key, out object value);
  void SetAttribute(string key, object value);
  // void RemoveAttribute(string key)
}

public class DynamicObject: IDynamicObject {
  private readonly Dictionary<string, object> data = new Dictionary<string, object>(StringComparer.Ordinal);

  bool IDynamicObject.TryGetAttribute(string key, out object value) {
    return data.TryGet(key, out value);
  }

  void IDynamicObject.SetAttribute(string key, object value) {
    data[key] = value;
  }
}

And then, the trait methods can add and retrieve data if the "trait interface" inherits from IDynamicObject:

public class Client: DynamicObject { /* implementation see above */ }

public interface TClientWeight, IDynamicObject {
  double Weight { get; }
}

public class ClientA: Client, TClientWeight { }

public static class TClientWeightMethods {
  public static bool HasWeightChanged(this TClientWeight client) {
    object oldWeight;
    bool result = client.TryGetAttribute("oldWeight", out oldWeight) && client.Weight.Equals(oldWeight);
    client.SetAttribute("oldWeight", client.Weight);
    return result;
  }
  // add more methods as you see fit
}

Note: by implementing IDynamicMetaObjectProvider as well the object would even allow to expose the dynamic data through the DLR, making the access to the additional properties transparent when used with the dynamic keyword.

Lucero
  • 59,176
  • 9
  • 122
  • 152
  • 9
    So you're saying put all the data in the base class, and all the method implementations in extension methods that have hooks on the interfaces? It's curious solution, but perhaps workable. My only beef is that you're making the client classes carry a lot of "dead weight" (unused members). With some fancy serialization it won't need to be saved to disk, but it's still consuming memory. – mpen May 23 '12 at 23:46
  • 2
    "Sort of". I sure can't think of anything better within the C# language, so +1. I do not give this the same footing as a Trait, however. (A sever limitation is outlined by Mark.) –  May 23 '12 at 23:46
  • Err.. I guess with C# properties I only have to implement the property for each derived class and I can store the data there. It's a little bit redundant, but I guess it's better than re-implementing all the methods too. – mpen May 23 '12 at 23:48
  • To complete this answer, I'd still like to see you define a concrete member variable (all I see is properties). I'm not sure if you intend for me to define them in `Client`, or redefine them multiple times in `ClientB` and `ClientC` as needed. – mpen May 23 '12 at 23:58
  • 1
    @Mark, see my updates for dynamic data storage (implementing the serialization is left as an excercise to the reader ;) ). Since interfaces cannot define contracts for fields you cannot use fields as part of the "trait", but of course the properties can be read-write! I'm not saying that C# has traits, but rather that the extension methods can serve as reusable code blocks for interfaces, so that re-implementation of the methods is not required; of course the code has to have all needed members readily available on the interface. – Lucero May 24 '12 at 00:07
  • @Lucero awesome approach! Thanks for sharing it. I wonder, why your solution hasn't been accepted, since there is no other way to answer this question with standard C# means. – Maxwin Apr 05 '13 at 11:03
  • @NullAndVoid, glad to know that you like it! I don't know either why no solution (including mine) has been accepted though. – Lucero Apr 06 '13 at 21:44
  • @Lucero `ConditionalWeakTable` will allow you to store additional data without controlling `Client`'s base class. Essentially, you can add a `static` `ConditionalWeakTable` to `TClientWeightMethods` and weak-key on `TClientWeight`. I imagine this technique wasn't available when you wrote your answer, but now that it is, I figured it was share-worthy. – Keen May 29 '14 at 20:26
  • @Lucero: The first line of your usage example is `var c1 = new Class1();`. Think you meant `var c1 = new ClassA();` instead? Thanks for your helpful answer! – DWright Oct 05 '14 at 18:25
  • Thanks for the great and clever answer! Unfortunately, this approach will not work if you have more than one stateful trait because C# do not support multiple inheritance. – Marat Asadurian Jun 01 '16 at 20:08
  • Is there a mistake? How `ca.IsHeavierThan(10)` returns **OK**, while `ClientA` doesnt use either `TClientWeightMethods` or other classes ??? – T.Todua Jan 28 '18 at 21:12
  • Where to store data with traits? As @Stijn says: Traits are coming to C# in the form of default interface methods (probably in C# 8.0). Traits can only implement methods, including getters and setters, but cannot store any state. But they can force a class to implement properties. E.g.: `interface NameTrait { string FirstName { get; set; } string LastName { get; set; } string FullName => $"{FirstName} {LastName}"; }`. The class must implement (and store) `FirstName` and `LastName`. The trait implements `FullName`. This is how traits are supposed to work. – Olivier Jacot-Descombes Sep 20 '18 at 11:16
17

Traits can be implemented in C# 8 by using default interface methods. Java 8 introduced default interface methods for this reason too.

Using C# 8, you can write almost exactly what you proposed in the question. The traits are implemented by the IClientWeight, IClientHeight interfaces that provide a default implementation for their methods. In this case, they just return 0:

public interface IClientWeight
{
    int getWeight()=>0;
}

public interface IClientHeight
{
    int getHeight()=>0;
}

public class Client
{
    public String Name {get;set;}
}

ClientA and ClientB have the traits but don't implement them. ClientC implements only IClientHeight and returns a different number, in this case 16 :

class ClientA : Client, IClientWeight{}
class ClientB : Client, IClientHeight{}
class ClientC : Client, IClientWeight, IClientHeight
{
    public int getHeight()=>16;
}

When getHeight() is called in ClientB through the interface, the default implementation is called. getHeight() can only be called through the interface.

ClientC implements the IClientHeight interface so its own method is called. The method is available through the class itself.

public class C {
    public void M() {        
        //Accessed through the interface
        IClientHeight clientB = new ClientB();        
        clientB.getHeight();

        //Accessed directly or through the class
        var clientC = new ClientC();        
        clientC.getHeight();
    }
}

This SharpLab.io example shows the code produced from this example

Many of the traits features described in the PHP overview on traits can be implemented easily with default interface methods. Traits (interfaces) can be combined. It's also possible to define abstract methods to force classes to implement certain requirements.

Let's say we want our traits to have sayHeight() and sayWeight() methods that return a string with the height or weight. They'd need some way to force exhibiting classes (term stolen from the PHP guide) to implement a method that returns the height and weight :

public interface IClientWeight
{
    abstract int getWeight();
    String sayWeight()=>getWeight().ToString();
}

public interface IClientHeight
{
    abstract int getHeight();
    String sayHeight()=>getHeight().ToString();
}

//Combines both traits
public interface IClientBoth:IClientHeight,IClientWeight{}

The clients now have to implement thet getHeight() or getWeight() method but don't need to know anything about the say methods.

This offers a cleaner way to decorate

SharpLab.io link for this sample.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • 8
    The fact that you need to cast it to the interface type seems to make the code alot more verbose. Do you know the reasons it was designed like this? – Barsonax May 16 '19 at 21:23
  • 4
    @Barsonax From the [docs](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#default-interface-methods) it seems that the main reasons for implementing were API development and backward compatibility and interoperation with Swift and Android, not as a language feature for traits / mixins. I totally agree casting to the interface is an annoyance if you are looking for a mixins/traits/multiple inheritance style language features. Shame. – MemeDeveloper May 13 '20 at 00:08
  • 2
    @MemeDeveloper and those features in Java *are* used for traits and mixins and versioning. The `what's new` page is just a short description and doesn't contain the reasons. You'll find those in the CSharplang Github repo, in the design meetings. The AndroidSDK uses DIMs to implement traits, and now, so does C#. OTOH, Android SDK interoperability is probably the most important motivation for this feature – Panagiotis Kanavos May 13 '20 at 12:57
  • @MemeDeveloper as for the syntax, it's a necessity, not an annoyance, to avoid inheritance triangles. Explicit interface implementations use the same syntax, for the same reasons - avoid collisions when two interfaces provide the same member. – Panagiotis Kanavos May 13 '20 at 12:59
  • @PanagiotisKanavos Thanks yes. I mean I am sure there are very good reasons for all the decisions, it's just that personally from a more surface perspective I'd love to be able to "compose" classes in C# from multiple reusable bits of code without the need for boilerplate code (as I can in e.g. Python (multiple inheritance) or less (mixins)). – MemeDeveloper May 13 '20 at 16:16
  • 2
    It seems to me (a language architecture layman) that there need not be any major issues supporting this in C#. Surely the compiler could just handle a bit like partial classes - i.e. the compiler can Error if there's multiple definitions of same thing. Seems like should be really straightforward and would make my work days a lot more efficient. Anyhow I guess I can get something to work with Fody or similar. I just like to keep it minimal and DRY, and often find myself going to great lengths to get around this limitation in C#. – MemeDeveloper May 13 '20 at 16:19
  • 1
    One of the reasons why inherited 'trait' implementations must be accessed via an explicit interface reference is to avoid the potential [diamond problem](https://en.wikipedia.org/wiki/Multiple_inheritance) - more than one base interface / trait could expose the same method signature. – StuartLC Sep 02 '21 at 11:51
  • Interestingly the pitch made positive mention of traits: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods . Sadly this needless crippling of the feature removes such powerful usefulness. Diamond problem: Super easy solution: when a class has multiple interface implementations, forbid default implemented interface members from having any overlap (IFoo and IBar can't both implement `int GetFoo() => ...` signature). Easy peasy. – Nicholas Petersen Nov 04 '21 at 14:30
10

C# language (at least to version 5) does not have support for Traits.

However, Scala has Traits and Scala runs on the JVM (and CLR). Therefore, it's not a matter of run-time, but simply that of the language.

Consider that Traits, at least at the Scala sense, can be thought of as "pretty magic to compile in proxy methods" (they do not affect the MRO, which is different from Mixins in Ruby). In C# the way to get this behavior would be to use interfaces and "lots of manual proxy methods" (e.g. composition).

This tedious process could be done with a hypothetical processor (perhaps automatic code generation for a partial class via templates?), but that's not C#.

Happy coding.

  • 1
    I'm not exactly sure what this answers. Are you suggesting that I should hack together something to pre-process my C# code? – mpen May 23 '12 at 23:32
  • @Mark No. I was 1) Suggesting C#, the language, cannot support it (although perhaps with dynamic proxies? This level of magic is beyond me.) 2) That Traits do not affect the MRO and can be "simulated by hand"; that is, a Trait can be flattened into every Class it is mixed into, as with Composition. –  May 23 '12 at 23:33
  • I don't know what an "MRO" is, can you explain that? And how would I "simulate them by hand"? That's what I'm asking about... I can't think of a nice way to encapsulate the extra functionality. – mpen May 23 '12 at 23:36
  • 2
    @Mark Ahh, Method Resolution Order. That is, Traits (again, in the Scala sense which are still based on Single Inheritance run-time) do not actually affect the class hierarchy. There is no "trait class" added to the [virtual] dispatch tables. The methods/properties in the Traits are copied (during completing) into the respective classes. Here is are some [papers about traits](http://scg.unibe.ch/research/traits) as used in Scala. Ordersky presents that Traits can be used in a SI runtime, which is why they are "baked in" at compilation. –  May 23 '12 at 23:38
  • 1
    @Mark This differs from a language like Ruby which will inject the "mixin" type (a form of traits) into the MRO (which is a form of alternating the class hierarchy, but with control and restrictions). –  May 23 '12 at 23:43
  • 3
    I'm hesitant to upvote you because you haven't provided me with anything concrete yet, just a lot of talk about other languages. I'm trying to figure out how I can borrow some of these ideas from Scala....but that's all built-in to the language. How's it transferable? – mpen May 23 '12 at 23:50
  • @Mark Don't worry -- it doesn't provide any insight into how to "solve task XYZ" in C#. I am merely playing the advocate saying that Traits are not part of C#. Lucero's answer is a practical solution which may be useful here. Just be careful not to "overdo" extension methods. BTDT. The use of Interfaces is crucial to that approach, and ensuing sanity. –  May 23 '12 at 23:52
  • Yes, but that much was covered in the first half of the first sentence in my question ;) Unless you're saying that these sorts of problems may be better-suited for a different language entirely (such as Scala). But that's quite a big shift from a language I otherwise love. – mpen May 23 '12 at 23:55
  • @Mark I get paid for using C# ;-) I have likes/dislike for both languages and both are suited for the same domain. That isn't to say I don't sorely miss certain aspects.... (from both, when in the other) –  May 24 '12 at 00:00
10

I'd like to point to NRoles, an experiment with roles in C#, where roles are similar to traits.

NRoles uses a post-compiler to rewrite the IL and inject the methods into a class. This allows you to write code like that:

public class RSwitchable : Role
{
    private bool on = false;
    public void TurnOn() { on = true; }
    public void TurnOff() { on = false; }
    public bool IsOn { get { return on; } }
    public bool IsOff { get { return !on; } }
}

public class RTunable : Role
{
    public int Channel { get; private set; }
    public void Seek(int step) { Channel += step; }
}

public class Radio : Does<RSwitchable>, Does<RTunable> { }

where class Radio implements RSwitchable and RTunable. Behind the scenes, Does<R> is an interface with no members, so basically Radio compiles to an empty class. The post-compilation IL rewriting injects the methods of RSwitchable and RTunable into Radio, which can then be used as if it really derived from the two roles (from another assembly):

var radio = new Radio();
radio.TurnOn();
radio.Seek(42);

To use radio directly before rewriting happened (that is, in the same assembly as where the Radio type is declared), you have to resort to extensions methods As<R>():

radio.As<RSwitchable>().TurnOn();
radio.As<RTunable>().Seek(42);

since the compiler would not allow to call TurnOn or Seek directly on the Radio class.

Pierre Arnaud
  • 10,212
  • 11
  • 77
  • 108
8

There is an academic project, developed by Stefan Reichart from the Software Composition Group at the University of Bern (Switzerland), which provides a true implementation of traits to the C# language.

Have a look at the paper (PDF) on CSharpT for the full description of what he has done, based on the mono compiler.

Here is a sample of what can be written:

trait TCircle
{
    public int Radius { get; set; }
    public int Surface { get { ... } }
}

trait TColor { ... }

class MyCircle
{
    uses { TCircle; TColor }
}
Pierre Arnaud
  • 10,212
  • 11
  • 77
  • 108
4

Building on what Lucero suggested, I came up with this:

internal class Program
{
    private static void Main(string[] args)
    {
        var a = new ClientA("Adam", 68);
        var b = new ClientB("Bob", 1.75);
        var c = new ClientC("Cheryl", 54.4, 1.65);

        Console.WriteLine("{0} is {1:0.0} lbs.", a.Name, a.WeightPounds());
        Console.WriteLine("{0} is {1:0.0} inches tall.", b.Name, b.HeightInches());
        Console.WriteLine("{0} is {1:0.0} lbs and {2:0.0} inches.", c.Name, c.WeightPounds(), c.HeightInches());
        Console.ReadLine();
    }
}

public class Client
{
    public string Name { get; set; }

    public Client(string name)
    {
        Name = name;
    }
}

public interface IWeight
{
    double Weight { get; set; }
}

public interface IHeight
{
    double Height { get; set; }
}

public class ClientA : Client, IWeight
{
    public double Weight { get; set; }
    public ClientA(string name, double weight) : base(name)
    {
        Weight = weight;
    }
}

public class ClientB : Client, IHeight
{
    public double Height { get; set; }
    public ClientB(string name, double height) : base(name)
    {
        Height = height;
    }
}

public class ClientC : Client, IWeight, IHeight
{
    public double Weight { get; set; }
    public double Height { get; set; }
    public ClientC(string name, double weight, double height) : base(name)
    {
        Weight = weight;
        Height = height;
    }
}

public static class ClientExt
{
    public static double HeightInches(this IHeight client)
    {
        return client.Height * 39.3700787;
    }

    public static double WeightPounds(this IWeight client)
    {
        return client.Weight * 2.20462262;
    }
}

Output:

Adam is 149.9 lbs.
Bob is 68.9 inches tall.
Cheryl is 119.9 lbs and 65.0 inches.

It isn't quite as nice as I'd like, but it's not too bad either.

Community
  • 1
  • 1
mpen
  • 272,448
  • 266
  • 850
  • 1,236
3

This is really an suggested extension to Lucero's answer where all the storage was in the base class.

How about using dependency properties for this?

This would have the effect of making the client classes light weight at run time when you have many properties that are not always set by every descendant. This is because the values are stored in a static member.

using System.Windows;

public class Client : DependencyObject
{
    public string Name { get; set; }

    public Client(string name)
    {
        Name = name;
    }

    //add to descendant to use
    //public double Weight
    //{
    //    get { return (double)GetValue(WeightProperty); }
    //    set { SetValue(WeightProperty, value); }
    //}

    public static readonly DependencyProperty WeightProperty =
        DependencyProperty.Register("Weight", typeof(double), typeof(Client), new PropertyMetadata());


    //add to descendant to use
    //public double Height
    //{
    //    get { return (double)GetValue(HeightProperty); }
    //    set { SetValue(HeightProperty, value); }
    //}

    public static readonly DependencyProperty HeightProperty =
        DependencyProperty.Register("Height", typeof(double), typeof(Client), new PropertyMetadata());
}

public interface IWeight
{
    double Weight { get; set; }
}

public interface IHeight
{
    double Height { get; set; }
}

public class ClientA : Client, IWeight
{
    public double Weight
    {
        get { return (double)GetValue(WeightProperty); }
        set { SetValue(WeightProperty, value); }
    }

    public ClientA(string name, double weight)
        : base(name)
    {
        Weight = weight;
    }
}

public class ClientB : Client, IHeight
{
    public double Height
    {
        get { return (double)GetValue(HeightProperty); }
        set { SetValue(HeightProperty, value); }
    }

    public ClientB(string name, double height)
        : base(name)
    {
        Height = height;
    }
}

public class ClientC : Client, IHeight, IWeight
{
    public double Height
    {
        get { return (double)GetValue(HeightProperty); }
        set { SetValue(HeightProperty, value); }
    }

    public double Weight
    {
        get { return (double)GetValue(WeightProperty); }
        set { SetValue(WeightProperty, value); }
    }

    public ClientC(string name, double weight, double height)
        : base(name)
    {
        Weight = weight;
        Height = height;
    }

}

public static class ClientExt
{
    public static double HeightInches(this IHeight client)
    {
        return client.Height * 39.3700787;
    }

    public static double WeightPounds(this IWeight client)
    {
        return client.Weight * 2.20462262;
    }
}
weston
  • 54,145
  • 21
  • 145
  • 203
0

This sounds like PHP's version of Aspect Oriented Programming. There are tools to help like PostSharp or MS Unity in some cases. If you want to roll-your-own, code-injection using C# Attributes is one approach, or as suggested extension methods for limited cases.

Really depends how complicated you want to get. If you are trying to build something complex I'd be looking at some of these tools to help.

RJ Lohan
  • 6,497
  • 3
  • 34
  • 54
  • Does AoP/PostSharp/Unity allow adding new members that become part of the *static* type system? (My limited AoP experience was just with annotation cut-points and similar..) –  May 24 '12 at 00:03
  • PostSharp rewrites the IL code and should be able to do that, yes. – Lucero May 24 '12 at 00:15
  • Yes I believe so, via aspects for member/interface introduction (at the IL level as noted). My experience is limited also, but I've not had much practical opportunity to get too deep into this approach. – RJ Lohan May 24 '12 at 00:38