Обновить
Комментарии 26
Хорошая статья, спасибо! Всегда полезно знать внутреннее устройство используемых механизмов
Честно говоря до этого думал, что DLR объект ничто иное как таблица
А за статью спасибо. Интересное чтиво.
Смешно про неделю получилось
Я флеш-разработчик, но давно поглядывал на C# — пробовал смотреть видеоуроки, а в начале этой недели получил 2 заказанные книги по C# — решил учить его. Так что, надеюсь, началась не неделя, а что-то более длинное :)
Спасибо, за статью.
Только не совсем понял про Target и L0 кеш. В виртуальной машине специально прописана оптимизация для CallSite, что Target всегда должен лежать в регистре? Ведь CallSite это по сути класс и место ему в куче, а адресация как у всех идёт через смещение.
Если что я имею в виду JIT
не совсем так )
L0 — это не кэш процессора. L0-2 кэши являются абстракциями в рамках архитектуры DLR. CLR понятия не имеет о CallSite как таковом. В отличие от нового ThreadPool в CLR 4 необходимого для TPL, DLR не затрагивает изменения в виртуальной машине.

Насчет JIT: конструкция вида

dynamic a = 2;
dynamic b = 3;
dynamic c = a + b;

при генерации делегата динамического узла практически будет эквивалентна статическому коду C#, т.е. будет создано лишь несколько машинных инструкций.

p.s.
еще кое-что о кэшах DLR )
если L0 — это непосредственно динамический метод, то
L1 — совокупность последних 10 использованных динамических методов, т.е. при нахождении нового типа объектов, вместо генерации нового динамического метода и обновления Update, DLR ищет эквивалентный метод в L1.
L2 — т.к. каждому динамическому узлу требуется 1 делегат и 1 экземпляр конкретного байндера, то L2 хранит делегаты с обобщенным экземпляром байндера. на данный момент L2 может хранить до 100 делегатов.
Спасибо, теперь стало понятно.
Я так понимаю, это некий аналог JIT-компилятора, только применительно к динамической типизации? А отличие лишь в том, что JIT-компилятор компилирует вызовы единожды (и если уже скомпилирован, вызывает напрямую), а в рассматриваемом DLR polymorphic inline cache собирает вызываемый кусок кода только в случае промаха кеша (а в противном случае дергает напрямую делегат).
Неясно только, как можно вообще закешировать такую штуку, как операция над объектом неизвестного типа. Что является «ключом» кеша? Комбинация {реальный тип объекта; выражение, которое необходимо к нему применить}?
>>Я так понимаю, это некий аналог JIT-компилятора, только применительно к динамической типизации?

образно — да.

>>Неясно только, как можно вообще закешировать такую штуку, как операция над объектом неизвестного типа. Что является «ключом» кеша?

«ключом» является сигнатура вызовов и сам байндер с метаданными.
например для:

dynamic x = 2;
dynamic y = 3;

int c = x + y;

long d = x + y;


будет создан полиморфный делегат с таким кодом:

object GeneratedMethod(object x, object y) {
if (x is int && x is int)
return (object) ((int)x + (int)y);
if (x is long)
return (object) ((long)x + (long)y);
}
Здравствуйте.
У меня к вам вопрос по поводу dynamic + .NET Remoting.

Допустим есть интерфэйс IRemoteCom и его метод DoSomething()
на сервере есть реализация IRemoteCom и этого метода.
Обычное использование:
var rObj = (IRemoteCom)Activator.GetObject(typeof(IRemoteCom),"tcp://localhost:1002/Test");
rObj.DoSomething();

работает ожидаемо.

Но когда я хочу использовать dynamic,
dynamic dObj = (IRemoteCom)Activator.GetObject(typeof(IRemoteCom),"tcp://localhost:1002/Test");
dObj.DoSomething();

я натыкаюсь на исключение, которое говорит мне, что у прокси класса нет такого метода.

Теперь вопрос: существует ли возможность научить dynamic вызывать методы через прокси? И как это сделать.

Спасибо.
Здравствуйте!

>>существует ли возможность научить dynamic вызывать методы через прокси? И как это сделать.

Как вариант можно использовать reflection. для этого необходимо создать динамический прокси-класс.
Например:

public class DynamicProxy : System.Dynamic.DynamicObject
{
    private readonly object _instance;

    public DynamicProxy(object instance)
    {
        _instance = instance;
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
    {
        if (binder.Name == "DoSomething")
        {
            var objType = _instance.GetType();

            var remoteInterface = objType.GetInterface("IRemoteCom");
            var method = remoteInterface.GetMethod("DoSomething");
            result = method.Invoke(_instance, args);

            return true;
        }

        result = null;
        return false;
    }
}

используя binder.Name можно проверять или нет список разрешенных объектов, а можно и вообще как параметр для remoteInterface.GetMethod(binder.Name).

надеюсь это поможет решить проблему.
:)
Спасибо. Я находил это решение на StackOverflow.
Моя проблемма в том, что я не знаю интерфейса (ни типа, ни имени).
Существует ли решение в этом случае?
тогда нет, т.к. при отсутствии метаданных DLR не сможет сгенерировать ничего.
а вытащить из прокси эти данные невозможно?
что нибудь типа GetRealType?
с таким не встречался, но наверное Вам стоит посмотреть в сторону WCF?
Попробую, мы как раз на .NET4 перешли.
Спасибо за советы.
Пишу интерпритатор своего языка на C#… Сначала юзал просто Object'ы, прочел статью, вспомнил про dynamic, переписал все под него, но вот проблема: все начало работать очень медленно… Решил продолжить юзать Object'ы (:
весьма странно — вы используете reflection или code emitter вместо dynamic? просто используя pic падение производительности ~15% по сравнению с типизированным кодом.
Немного не понял, что вы имеете ввиду, не мастер в этом… Когда я здесь:
	public class Tocken
	{
		public TockenType Type { get; private set; }
		public object Value { get; private set; }
		
		public Tocken (object Value, TockenType Type)
		{
			this.Value = Value;
			this.Type = Type;
		}
	}

И здесь:
	public class Node
	{
		public NodeKind Kind;
		public Object Value;
		public List<Node> Nodes;

		public Node (NodeKind Kind, Node Value,  List<Node> Nodes)
		{
			this.Kind = Kind;
			this.Value = Value;
			this.Nodes = Nodes;
		}
		
		public Node (NodeKind Kind, Object Value)
		{
			this.Kind = Kind;
			this.Value = Value;
			this.Nodes = new List<Node>();
		}

		public void Write (int depth)
		{
			for (int i = 0; i < depth; i++)
				Console.Write ('|');
			Console.Write ('+' + Kind.ToString ().ToUpper ());

			if (Value != null)
			if (Value.GetType () == typeof(Node)) {
				Console.WriteLine();
				(Value as Node).Write (depth + 1);
			} else {
				if (Value != null)
					Console.Write("={1}", Kind.ToString ().ToUpper (), Value.ToString ());
			}
			foreach (Node n in Nodes) {
				Console.WriteLine();
				n.Write(depth + 1);
			}

			if(depth == 0)
				Console.WriteLine();
		}
	}

Менял Object на dynamic, синтаксическое дерево этого кода:
def a = 5
строилось около 3 секунд вместо где-то четверти секунды…

P.S. Компилится под моно
Во-первых, 2 ошибки в написании слов — это перебор (интерпретация и token).

Во-вторых, возможно, JIT еще не прошелся с DLR.

В-третьих, (Value as Node), где Value — dynamic — будет вызван метод TryConvert (если это DynamicObject), если нет то отдельный call site будет обслуживать это дело, т.е. MSIL инструкция isinst не будет задействована.

Также при создании объектов Mono проигрывает CLR на ~50%.
Ну с token'ом — это привычка :| А интерпретация, сам не знаю…
Спасибо за ответ…
думаю, лучше пройтись профайлером, т.к. результаты могут зависеть еще и от входных данных.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.