Я знаю, что этот вопрос касается bash
, но - только для записи - ksh93
умнее и реализует его, как ожидалось:
$ ksh -c 'i=5; for x in {1..$i}; do echo "$x"; done'
1
2
3
4
5
$ ksh -c 'echo $KSH_VERSION'
Version JM 93u+ 2012-02-29
$ bash -c 'i=5; for x in {1..$i}; do echo "$x"; done'
{1..5}
Вы можете создать DynamicObject
, который перенаправляет полученные вызовы в список целей в стиле цепочки ответственности (обратите внимание, что полиморфная диспетчеризация также работает так - от самого производного класса и выше):
public class Composition : DynamicObject {
private List<object> targets = new List<object>();
public Composition(params object[] targets) {
AddTargets(targets);
}
protected void AddTargets(IEnumerable<object> targets) {
this.targets.AddRange(targets);
}
public override bool TryInvokeMember(
InvokeMemberBinder binder, object[] args, out object result) {
foreach (var target in targets) {
var methods = target.GetType().GetMethods();
var targetMethod = methods.FirstOrDefault(m =>
m.Name == binder.Name && ParametersMatch(m, args));
if (targetMethod != null) {
result = targetMethod.Invoke(target, args);
return true;
}
}
return base.TryInvokeMember(binder, args, out result);
}
private bool ParametersMatch(MethodInfo method, object[] args) {
var typesAreTheSame = method.GetParameters().Zip(
args,
(param, arg) => param.GetType() == arg.GetType());
return typesAreTheSame.Count() == args.Length &&
typesAreTheSame.All(_=>_);
}
}
Обратите внимание, что вы также хотите реализовать делегирование для свойств (TryGetMember
и TrySetMember
), индексаторов (TryGetIndex
и TrySetIndex
) и операторов (TryBinaryOperation
и TryUnaryOperation
).
Затем, учитывая набор классов:
class MyClass {
public void MyClassMethod() {
Console.WriteLine("MyClass::Method");
}
}
class MyOtherClass {
public void MyOtherClassMethod() {
Console.WriteLine("MyOtherClass::Method");
}
}
Вы можете «смешать» их все вместе:
dynamic blend = new Composition(new MyClass(), new MyOtherClass());
blend.MyClassMethod();
blend.MyOtherClassMethod();
Вы также можете расширить динамический объект, чтобы использовать атрибуты классов или другие виды аннотаций для поиска миксинов. Например, учитывая этот интерфейс аннотации:
public interface Uses<M> where M : new() { }
Вы можете иметь это DynamicObject
:
public class MixinComposition : Composition {
public MixinComposition(object target) :
base(target) {
AddTargets(ResolveMixins(target.GetType()));
}
private IEnumerable<object> ResolveMixins(Type mainType) {
return ResolveMixinTypes(mainType).
Select(m => InstantiateMixin(m));
}
private IEnumerable<Type> ResolveMixinTypes(Type mainType) {
return mainType.GetInterfaces().
Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(Uses<>)).
Select(u => u.GetGenericArguments()[0]);
}
private object InstantiateMixin(Type type) {
return Activator.CreateInstance(type);
}
}
И создавать свои «смеси», например:
class MyMixin {
public void MyMixinMethod() {
Console.WriteLine("MyMixin::Method");
}
}
class MyClass : Uses<MyMixin> {
public void MyClassMethod() {
Console.WriteLine("MyClass::Method");
}
}
...
dynamic blend = new MixinComposition(new MyClass());
blend.MyClassMethod();
blend.MyMixinMethod();
Вы можете создавать миксиноподобные конструкции в C # 4.0, не используя динамические, с методами расширения на интерфейсах и классом ConditionalWeakTable
для хранения состояния. Посмотрите здесь за идею.
Вот пример:
public interface MNamed {
// required members go here
}
public static class MNamedCode {
// provided methods go here, as extension methods to MNamed
// to maintain state:
private class State {
// public fields or properties for the desired state
public string Name;
}
private static readonly ConditionalWeakTable<MNamed, State>
_stateTable = new ConditionalWeakTable<MNamed, State>();
// to access the state:
public static string GetName(this MNamed self) {
return _stateTable.GetOrCreateValue(self).Name;
}
public static void SetName(this MNamed self, string value) {
_stateTable.GetOrCreateValue(self).Name = value;
}
}
Используйте его так:
class Order : MNamed { // you can list other mixins here...
...
}
...
var o = new Order();
o.SetName("My awesome order");
...
var name = o.GetName();
Проблема использования атрибута заключается в том, что вы не можете передавать общие параметры из класса в Mixin. Вы можете сделать это с помощью маркеров интерфейсов.
Я знаю, что это старая тема, но я также хотел бы представить проект с открытым исходным кодом, над которым я сейчас работаю: mixinSharp .
Это расширение рефакторинга на основе Roslyn для Visual Studio 2015, которое добавляет поддержку миксинов в C #, генерируя необходимый код делегирования.
Например, допустим, у вас есть следующий миксин-код, который вы хотите использовать повторно:
// mixin class with the code you want to reuse
public class NameMixin
{
public string Name { get; set; }
public void DoSomething() { }
}
И данный дочерний класс, в который вы хотите включить свой миксин:
// child class where the mixin should be included
public class Person
{
// reference to the mixin
private NameMixin _name = new NameMixin();
}
Если вы выполните шаг рефакторинга mixinSharp в поле NameMixin _name
, расширение автоматически добавит весь клейкий код, необходимый для включения mixin в ваш класс:
public class Person
{
// reference to the mixin
private NameMixin _name = new NameMixin();
public string Name
{
get { return _name.Name; }
set { _name.Name = value; }
}
public void DoSomething() => _name.DoSomething();
}
Помимо этого, в mixinSharp есть некоторые дополнительные такие функции, как внедрение конструктора для экземпляров mixin, реализация интерфейсов с помощью mixin и многое другое.
Источники доступны на github , а двоичные файлы (скомпилированное расширение Visual Studio) доступны в Visual Studio Gallery .
Я работал над открытым фреймворком Mixin для C # pMixins . Он использует частичные классы и генераторы кода, чтобы связать класс Mixin с целью:
//Mixin - Class that contains members that should be injected into other classes.
public class Mixin
{
// This method should be in several class
public void Method(){ }
}
//Target (Note: That it is partial) - Add members from Mixin
[pMixn(Target = typeof(Mixin)]
public partial class Target{}
//Example of using Target
public class Consumer
{
public void Example()
{
var target = new Target();
// can call mixed in method
target.Method();
// can implicitly convert Target to Mixin
Mixin m = new Target();
m.Method();
}
}
Я работал над проектом в 2008 году, используя библиотеку стилей внедрения зависимостей, которая позволяла нам определять дизайн нашего приложения (в коде), используя внутренний язык, специфичный для предметной области (DSL).
Библиотека позволяет нам определять системы и составлять эти системы из других систем. Система представляла собой набор объектов, которые реализовали интерфейсы в области видимости. Система / подсистема может выбрать предоставление интерфейсов родительской области.
Эффектом этого было то, что миксин пришел бесплатно. Вы просто добавили бы класс, реализующий часть поведения, к определению вашей системы и предоставили бы его интерфейс родительской области видимости. Эта система теперь имеет такое поведение.
Возможно, вы сможете сделать это и с помощью современных систем внедрения зависимостей.
Мы использовали NDI ( https://github.com/NigelThorne/ndependencyinjection/wiki ).
Примечание: я написал NDI еще в 2008 году.