2011-02-07 3 views
38

Я хотел бы реализовать метод со следующей подписьюC#: Динамическое время выполнения броска

dynamic Cast(object obj, Type castTo); 

Любой знает, как сделать это? obj определенно реализует castTo, но его нужно отдать должным образом, чтобы некоторые из файлов привязки приложения моего приложения работали.

Edit: Если некоторые из ответов не имеет смысла потому, что я изначально случайно набранный dynamic Cast(dynamic obj, Type castTo); - я имею в виду вход должен быть object или какой-либо другой гарантируется базовый класс

+0

Вы говорите, что вам нужно динамически вызывать неявный или явный оператор преобразования? – Gabe

+0

либо один, но на данный момент, явный –

ответ

58

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

  • Кастинг: акт изменения типа ссылки, указывающей на объект. Либо перемещение вверх, либо вниз по иерархии объектов или к реализованному интерфейсу
  • Преобразование: создание нового объекта из исходного исходного объекта другого типа и доступ к нему посредством ссылки на этот тип.

Часто бывает трудно узнать разницу между 2 в C#, потому что оба они используют один и тот же оператор C#: литой.

В этой ситуации вы почти наверняка не ищете операцию литья.Листинг dynamic другому dynamic по существу является преобразованием идентичности. Он не дает никакой ценности, потому что вы просто получаете ссылку dynamic обратно к одному и тому же базовому объекту. Результат не изменится.

Вместо этого вы хотите, чтобы в этом сценарии было преобразование. Это преобразование базового объекта в другой тип и доступ к результирующему объекту в модуле dynamic. Лучший API для этого - Convert.ChangeType.

public static dynamic Convert(dynamic source, Type dest) { 
    return Convert.ChangeType(source, dest); 
} 

EDIT

Обновленный вопрос имеет следующую строку:

OBJ определенно реализует castTo

Если это так, то метод Cast не должны существовать. Источнику object можно просто присвоить ссылку dynamic.

dynamic d = source; 

Похоже, что вы пытаетесь сделать, чтобы увидеть конкретный интерфейс или тип в иерархии source через dynamic ссылки. Это просто невозможно. Полученная в результате ссылка dynamic увидит объект реализации напрямую. Он не просматривает какой-либо конкретный тип в иерархии источника. Таким образом, идея литья другого типа в иерархии, а затем обратно в dynamic точно совпадает с назначением только dynamic. Он все равно будет указывать на один и тот же базовый объект.

+0

Привет, Джаред, я ищу кастинг, но вы делаете хороший момент, я опечатывал вопрос, он должен быть от объекта (или другого базового класса) до динамического. Сейчас исправит. –

+1

@George в этом случае, почему бы просто не просто нажать на 'dynamic' напрямую? Там не должно быть необходимости в этом методе, если базовая операция действительно является литой – JaredPar

+0

, прецедентом является объект BaseType, и у меня есть обработчики IList , где каждый обработчик реализует IHandle , где T: BaseType. Мне нужно запустить все обработчики, которые применяются к этому конкретному типу, поскольку динамика не может правильно догадаться (возможно, потому, что IHandle ковариант). Я получил его работу, используя отражение, но oy vey. –

0

Попробуйте родовым:

public static T CastTo<T>(this dynamic obj, bool safeCast) where T:class 
{ 
    try 
    { 
     return (T)obj; 
    } 
    catch 
    { 
     if(safeCast) return null; 
     else throw; 
    } 
} 

Это в формате метода расширения, так что его использование было бы, как если бы она была членом динамических объектов:

dynamic myDynamic = new Something(); 
var typedObject = myDynamic.CastTo<Something>(false); 

EDIT: Grr, не видел этого. Да, вы можете рефлекторно закрыть общий характер, и это не было бы трудно скрыть в необщего метод расширения:

public static dynamic DynamicCastTo(this dynamic obj, Type castTo, bool safeCast) 
{ 
    MethodInfo castMethod = this.GetType().GetMethod("CastTo").MakeGenericMethod(castTo); 
    return castMethod.Invoke(null, new object[] { obj, safeCast }); 
} 

Я просто не уверен, что вы получите из этого. В основном вы используете динамический режим, заставляя актеры отражать тип, а затем набирая его обратно в динамике. Может быть, ты прав, я не должен спрашивать. Но это, вероятно, сделает то, что вы хотите. В основном, когда вы идете в динамичную страну, вы теряете необходимость выполнять большинство операций литья, так как вы можете обнаружить, что такое объект, и делает он с помощью рефлексивных методов или проб и ошибок, поэтому не так много элегантных способов сделать это.

+1

Это не работает (без отражения), если 'T' не известно при компиляции. – jason

+0

@KiethS, Тип предоставляется во время выполнения. Я знаю, не спрашивай. Полагаю, я мог бы написать это и использовать рефлексию, чтобы закрыть общий, но должен быть лучший способ. –

+1

@George: Я не верю, что вы сможете обойтись здесь, используя отражение. –

-2

В качестве альтернативы:

public static T Cast<T>(this dynamic obj) where T:class 
{ 
    return obj as T; 
} 
+2

Требование состоит в том, что тип указан во время выполнения. – Jimmy

20

Это должно работать:

public static dynamic Cast(dynamic obj, Type castTo) 
{ 
    return Convert.ChangeType(obj, castTo); 
} 

Редактировать

я написал следующий тестовый код:

var x = "123"; 
var y = Cast(x, typeof(int)); 
var z = y + 7; 
var w = Cast(z, typeof(string)); // w == "130" 

Он похож на тип «typecasting», который можно найти в таких языках, как PHP, JavaScript или Python (потому что он также преобразует значение в желаемый тип). Я не знаю, хорошо ли это, но это, безусловно, работает ... :-)

+0

castTo должен описывать ValueType, чтобы это работало. – KeithS

+0

Хм, собираюсь попробовать, но разве это не всегда будет возвращать этот унылый объект? –

+0

@Keith: просто протестировал его, и он работает с 'String' (который является классом, а не типом значения). – rsenna

7

Лучшее, что я получил до сих пор:

dynamic DynamicCast(object entity, Type to) 
{ 
    var openCast = this.GetType().GetMethod("Cast", BindingFlags.Static | BindingFlags.NonPublic); 
    var closeCast = openCast.MakeGenericMethod(to); 
    return closeCast.Invoke(entity, new[] { entity }); 
} 
static T Cast<T>(object entity) where T : class 
{ 
    return entity as T; 
} 
+0

Довольно многое из того, что я закончил после вашего первоначального комментария. – KeithS

+2

Это не имеет смысла для меня. Как вызов 'DynamicCast (obj, typeof (Foo))' любой отличается от '(dynamic) obj', за исключением того, что вы получаете нулевую ссылку в случае, когда' obj' (который имеет *** компиляцию-время ** * type 'object') не является' Foo'? Кроме того, ограничение 'where T: object' бессмысленно и запрещено. –

+1

@JeppeStigNielsen Это вопрос бокса. Предположим, что у вас есть «Человек» с методом «GetFullName» и «Professor: Person», который переопределяет «GetFullName», чтобы суффиктировать его «PhD». Теперь предположим, что сущность имеет тип 'Профессор', но отбрасывается как« Личность »вне нашего метода, может быть, это то, как ее загружают ORM или связующее устройство или что-то в этом роде. если вы вызываете '((динамическое) право) .GetFullName()' он будет запускать 'Person.GetFullName()'. Прошло много времени с тех пор, как эта проблема возникла для меня, поэтому особенности могут быть немного неправильными, но это связано с этим. –

6

Рамки с открытым исходным кодом Dynamitey имеет статический метод, который делает позднее связывание с использованием DLR включая преобразование литой среди others.

dynamic Cast(object obj, Type castTo){ 
    return Dynamic.InvokeConvert(obj, castTo, explict:true); 
} 

Преимущество этого над Cast<T> называется с помощью отражения, что это будет также работать для любого IDynamicMetaObjectProvider, который имеет динамические операторы преобразования, т. TryConvert на DynamicObject.

3

Я понимаю, что на это был дан ответ, но я использовал другой подход и думал, что это может стоить обмена. Кроме того, я чувствую, что мой подход может вызвать нежелательные накладные расходы. Тем не менее, я не могу наблюдать или подсчитывать что-либо, что происходит, что плохо при нагрузках, которые мы наблюдаем. Я искал любую полезную обратную связь по этому подходу.

Проблема с динамикой заключается в том, что вы не можете напрямую подключать любые функции к динамическому объекту. Вы должны использовать что-то, что может определить назначения, которые вы не хотите анализировать каждый раз.

При планировании этого простого решения я посмотрел, какие действительные посредники при попытке перепечатать похожие объекты. Я обнаружил, что двоичный массив, строка (xml, json) или жесткое кодирование преобразования (IConvertable) были обычными подходами. Я не хочу получать двоичные конверсии из-за коэффициента поддерживаемости кода и лени.

Моя теория заключалась в том, что Newtonsoft мог бы сделать это, используя строковый посредник.

В качестве недостатка я вполне уверен, что при преобразовании строки в объект, чтобы она использовала отражение, путем поиска текущей сборки для объекта с соответствующими свойствами, создайте тип, а затем создайте свойства, которые потребуются больше отражение. Если это правда, все это можно считать предотвратимыми накладными расходами.

C#:

//This lives in a helper class 
public static ConvertDynamic<T>(dynamic data) 
{ 
    return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(Newtonsoft.Json.JsonConvert.SerializeObject(data)); 
} 

//Same helper, but in an extension class (public static class), 
//but could be in a base class also. 
public static ToModelList<T>(this List<dynamic> list) 
{ 
    List<T> retList = new List<T>(); 
    foreach(dynamic d in list) 
    { 
     retList.Add(ConvertDynamic<T>(d)); 
    } 
} 

С учетом сказанного, это соответствует другой утилиты я вместе взятые, что позволяет мне сделать какой-либо объект в динамический. Я знаю, что я должен был использовать отражение, чтобы сделать это правильно:

public static dynamic ToDynamic(this object value) 
{ 
    IDictionary<string, object> expando = new ExpandoObject(); 

    foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType())) 
     expando.Add(property.Name, property.GetValue(value)); 

    return expando as ExpandoObject; 
} 

Я должен был предложить эту функцию. Произвольный объект, назначенный динамической типизированной переменной, не может быть преобразован в IDictionary и будет разорвать функцию ConvertDynamic. Для использования этой функциональной цепочки должна быть предоставлена ​​динамика объекта System.Dynamic.ExpandoObject или IDictionary <, объект >.

Смежные вопросы