Disclaimer: ChatGPT helped me to write this markdown file
This proposal introduces a new language feature for C# called Trait Implementations. This feature allows developers to provide implementations for interfaces on types that they don't have access to. Trait Implementations can also be used to extend existing interfaces with default implementations.
In C#, if a developer wants to implement an interface on a type, they must have access to the source code of that type. However, in some cases, a developer may want to add functionality to a type that they don't have access to, such as a third-party library. This can be achieved by creating a wrapper class that implements the interface and delegates to the original type, but this can be cumbersome and add unnecessary complexity.
Trait Implementations provide a simpler way to add functionality to types that a developer doesn't have access to. This can improve code maintainability and reduce boilerplate code.
The syntax for defining a Trait Implementation is similar to that of implementing an interface:
impl InterfaceName for TypeName
{
// implementation
}
The impl
keyword is used to define the Trait Implementation, followed by the name of the interface to be implemented and the name of the type to implement it on.
The implementation itself is contained within curly braces.
Trait Implementations can also be used to extend existing interfaces with default implementations.
This is done by using the where
keyword to specify a constraint on the generic type parameter:
impl TraitName for IAlreadyExistingInterface
{
// implementation
}
Here are some examples of how Trait Implementations could be used:
// define interface, that should act as trait
interface IActivatable
{
void SetActive(bool b);
}
// "trait implementation" for types you don't have access to.
impl IActivatable for Behaviour
{
void SetActive(bool b) => this.enabled = b;
}
This example shows how a developer can implement the IActivatable
interface on a third-party type Behaviour
using a Trait Implementation.
The implementation simply sets the enabled property of the Behaviour
instance to the value passed to the SetActive
method.
// "trait implementation" to extend an existing interface with a default implementation.
interface IInterpolateable<T>
{
T Lerp(T from, T to, float t);
}
impl IInterpolateable<Matrix4x4> for Matrix4x4
{
Matrix4x4 Lerp(Matrix4x4 from, Matrix4x4 to, float t)
{
return Matrix4x4.TRS(
Vector3.Lerp(from.transpose, to.transpose, t),
Quaternion.Lerp(from.rotation, to.rotation, t),
Vector3.Lerp(from.lossyScale, to.lossyScale, t)
);
}
}
// "trait implementation" to extend a generic interface with a default implementation.
impl IInterpolateable<INumber<T>> for INumber<T> where T : INumber<T>
{
T Lerp(T from, T to, float t) => (1 - t) * from + t * to;
}
This example shows how a developer can extend the IInterpolateable
interface with a default implementation for any type that implements the INumber<T>
interface.
The implementation uses the *
and +
operators to interpolate the values of the numbers using the Lerp method.
If a Trait Implementation provides a default implementation for an interface member, that implementation will be used when calling the member through a cast to the interface type. If the method is called through an instance of the target type, the explicit implementation provided by the target type will be used.
class A
{
public void MyMethod();
}
impl IMyTrait for A
{
public void MyMethod(){ ... }
}
A a = new A();
IMyTrait ia = a;
ia.MyMethod(); // Uses Trait Implementation
a.MyMethod(); // Uses A.MyMethod()
Any type that derives from a base type that has a Trait Implementation for an interface will automatically inherit the Trait Implementation for that interface.
This means that any type B
is considered to have a Trait T
as long as there is a Trait Implementation for T
defined for B
itself or any of its base types.
For example, if a Trait Implementation is defined for IMyTrait
on class A
, then any type that derives from A,
such as class B : A
, will also use the Trait Implementation for IMyTrait, even if it does not have its own Trait Implementation.
A a = new A();
B b = new B();
Assert.IsTrue(a is IMyTrait);
Assert.IsTrue((b as A) is IMyTrait);
Assert.IsTrue(b is IMyTrait);
Trait Implementations provide a flexible and powerful way for developers to add functionality to types that they don't have access to. They can also be used to extend existing interfaces with default implementations, which can improve code maintainability and reduce boilerplate code. The syntax for defining a Trait Implementation is simple and intuitive, and the design of the feature is straightforward and easy to understand. This feature would be a valuable addition to the C# language.