С#: динамическое время выполнения
Я хотел бы реализовать метод со следующей сигнатурой
dynamic Cast(object obj, Type castTo);
Кто-нибудь знает, как это сделать? obj определенно реализует castTo, но его нужно корректно нарисовать, чтобы некоторые из моих приложений для выполнения приложений выполнялись.
Изменить: если некоторые из ответов не имеют смысла, потому что я изначально случайно набрал dynamic Cast(dynamic obj, Type castTo);
- я имею в виду, что вход должен быть object
или какой-либо другой гарантированный базовый класс
Ответы
Ответ 1
Я думаю, вы путаете вопросы кастинга и преобразования здесь.
- Кастинг: акт изменения типа ссылки, указывающей на объект. Либо перемещение вверх, либо вниз по иерархии объектов или к реализованному интерфейсу.
- Преобразование: создание нового объекта из исходного исходного объекта другого типа и доступ к нему посредством ссылки на этот тип.
Часто бывает трудно узнать разницу между 2 в С#, потому что оба они используют один и тот же оператор С#: приведение.
В этой ситуации вы почти наверняка не ищете операцию трансляции. Передача a 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
в первую очередь. Он все равно укажет на один и тот же базовый объект.
Ответ 2
Это должно работать:
public static dynamic Cast(dynamic obj, Type castTo)
{
return Convert.ChangeType(obj, castTo);
}
Edit
Я написал следующий тестовый код:
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 (потому что он также преобразует значение в желаемый тип). Я не знаю, хорошо ли это, но это, безусловно, работает...: -)
Ответ 3
Лучший, который я получил до сих пор:
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;
}
Ответ 4
Рамка openource Dynamitey имеет статический метод, который связывает последнее время с использованием DLR, включая преобразование конверсий из другие.
dynamic Cast(object obj, Type castTo){
return Dynamic.InvokeConvert(obj, castTo, explict:true);
}
Преимущество этого над Cast<T>
, называемое использованием отражения, заключается в том, что это будет работать и для любого IDynamicMetaObjectProvider
, у которого есть операторы динамического преобразования, т.е. TryConvert на DynamicObject
.
Ответ 5
Я понимаю, что на это был дан ответ, но я использовал другой подход и думал, что это может стоить обмена. Кроме того, я чувствую, что мой подход может вызвать нежелательные накладные расходы. Тем не менее, я не могу наблюдать или подсчитывать что-либо, что происходит, что плохо при нагрузках, которые мы наблюдаем. Я искал любую полезную обратную связь по этому подходу.
Проблема с динамикой заключается в том, что вы не можете напрямую привязывать любые функции к динамическому объекту. Вы должны использовать что-то, что может определить назначения, которые вы не хотите определять каждый раз.
При планировании этого простого решения я посмотрел, какие действительные посредники при попытке повторить аналогичные объекты. Я обнаружил, что обычные подходы были двоичным массивом, строкой (xml, json) или жестким кодированием преобразования (IConvertable). Я не хочу попадать в двоичные преобразования из-за коэффициента поддерживаемости кода и лени.
Моя теория заключалась в том, что Newtonsoft мог бы сделать это, используя строковый посредник.
В качестве недостатка я вполне уверен, что при преобразовании строки в объект, чтобы она использовала отражение путем поиска текущей сборки для объекта с соответствующими свойствами, создайте этот тип, а затем создайте свойства, которые потребуют больше отражение. Если это правда, все это можно считать предотвратимыми накладными расходами.
С#:
//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 < string, object > .
Ответ 6
Попробуйте общий:
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 });
}
Я просто не уверен, что ты получишь. В основном вы используете динамический режим, заставляя актеры отражать тип, а затем набирая его обратно в динамике. Может быть, ты прав, я не должен спрашивать. Но это, вероятно, сделает то, что вы хотите. В основном, когда вы идете в динамичную страну, вы теряете необходимость выполнять большинство операций литья, так как вы можете обнаружить, что такое объект, и делает он с помощью рефлексивных методов или проб и ошибок, поэтому не так много элегантных способов сделать это.
Ответ 7
Я знаю, что это действительно старый пост, но вот простое решение для работы с dynamic
типом в c#
.
- можно использовать простое отражение для перечисления прямых свойств
- или можете использовать метод расширения
object
- или используйте
GetAsOrDefault<int>
чтобы получить новый строго типизированный объект со значением, если существует, или по умолчанию, если не существует.
public static class DynamicHelper
{
private static void Test( )
{
object myobj = new
{
myInt = 1,
myObjectArray = new object[ ]
{
"hello", "World"
},
myStringArray = new string[ ]
{
"hello", "World"
},
myIntArray = new[ ]
{
1, 2.3
},
myDict = new
{
myInt = 1
}
};
var stringArray = myobj.GetAsArrayOrDefault< string >( d => d.myStringArray );
var intArray = myobj.GetAsArrayOrDefault< string >( d => d.myIntArray );
var myIntOrZero = myobj.GetAsOrDefault< int >( p => p.myInt );
Console.WriteLine( $"stringArray: '{string.Join( ", ", stringArray )}'" );
Console.WriteLine( $"stringArray: '{string.Join( ", ", intArray )}'" );
Console.WriteLine( $"myIntOrZero: '{myIntOrZero}'" );
}
public static bool DoesPropertyExist( dynamic dyn, string property )
{
var t = ( Type )dyn.GetType( );
var props = t.GetProperties( );
return props.Any( p => p.Name.Equals( property ) );
}
public static TItem[ ] GetAsArrayOrDefault< TItem >( this object obj, Func< dynamic, object > test ) where TItem : class
{
try
{
var val = test( obj );
if( val is IEnumerable o )
{
var l = new List< TItem >( );
foreach( var i in o )
if( i is TItem item )
l.Add( item );
return l.ToArray( );
}
}
catch( RuntimeBinderException ) { }
return default( TItem[ ] );
}
public static T GetAsOrDefault< T >( this object obj, Func< dynamic, T > test )
{
try
{
var val = test( obj );
return ( T )val;
}
catch( RuntimeBinderException ) { }
return default( T );
}
}
Ответ 8
В качестве альтернативы:
public static T Cast<T>(this dynamic obj) where T:class
{
return obj as T;
}