Вот смешанная реализация, которую я только что придумал. Я вероятно буду использовать его с моей библиотекой .
Возможно, это было сделано где-то раньше.
Все это статически, без словарей или чего-то еще. Требуется немного дополнительного кода для каждого типа, вам не нужно никакого хранилища для каждого экземпляра. С другой стороны, это также дает вам гибкость, позволяя изменять реализацию миксинов на лету, если вы того пожелаете. Нет инструментов для пост-сборки, предварительной сборки и сборки.
Он имеет некоторые ограничения, но допускает такие вещи, как переопределение и т. Д.
Начнем с определения интерфейса маркера. Возможно, что-то будет добавлено к нему позже:
public interface Mixin {}
Этот интерфейс реализован миксинами. Миксины - это регулярные занятия. Типы не наследуют и не реализуют миксины напрямую. Вместо этого они просто выставляют экземпляр миксина, используя интерфейс:
public interface HasMixins {}
public interface Has<TMixin> : HasMixins
where TMixin : Mixin {
TMixin Mixin { get; }
}
Реализация этого интерфейса означает поддержку миксина. Важно, чтобы это было реализовано явно, поскольку у нас будет несколько таких типов для каждого типа.
Теперь немного трюка с использованием методов расширения. Мы определяем:
public static class MixinUtils {
public static TMixin Mixout<TMixin>(this Has<TMixin> what)
where TMixin : Mixin {
return what.Mixin;
}
}
Mixout
выставляет миксин соответствующего типа. Теперь, чтобы проверить это, давайте определим:
public abstract class Mixin1 : Mixin {}
public abstract class Mixin2 : Mixin {}
public abstract class Mixin3 : Mixin {}
public class Test : Has<Mixin1>, Has<Mixin2> {
private class Mixin1Impl : Mixin1 {
public static readonly Mixin1Impl Instance = new Mixin1Impl();
}
private class Mixin2Impl : Mixin2 {
public static readonly Mixin2Impl Instance = new Mixin2Impl();
}
Mixin1 Has<Mixin1>.Mixin => Mixin1Impl.Instance;
Mixin2 Has<Mixin2>.Mixin => Mixin2Impl.Instance;
}
static class TestThis {
public static void run() {
var t = new Test();
var a = t.Mixout<Mixin1>();
var b = t.Mixout<Mixin2>();
}
}
Довольно забавно (хотя в ретроспективе это имеет смысл), IntelliSense не обнаруживает, что метод расширения Mixout
применяется к Test
, но компилятор принимает его, пока Test
действительно имеет mixin , Если вы попробуете,
t.Mixout<Mixin3>();
Выдает ошибку компиляции.
Вы можете немного придумать и определить следующий метод:
[Obsolete("The object does not have this mixin.", true)]
public static TSome Mixout<TSome>(this HasMixins something) where TSome : Mixin {
return default(TSome);
}
Что это делает, а) отображает метод с именем Mixout
в IntelliSense, напоминая вам о его существовании, и б) предоставляет несколько более описательное сообщение об ошибке (генерируемое атрибутом Obsolete
).