using System.Diagnostics;
using System.Reflection;
namespace BytecodeApi;
///
/// Provides methods and properties serving as a general object manipulation helper class.
///
public static class CSharp
{
///
/// Returns the converted version of , if it is of the specified type; otherwise, returns ().
///
/// The type to which to convert to.
/// The to be converted.
///
/// The converted version of , if it is of the specified type; otherwise, returns ().
///
public static T? CastOrDefault(object? obj)
{
return obj is T castedObject ? castedObject : default;
}
///
/// Performs an and disposes , if is an . This is useful if the given only indirectly inherits and therefore the keyword cannot be used.
///
/// The to be disposed. If is not an , it will not be disposed.
/// The to be performed before the method is called. This is equivalent to the body of the statement.
public static void Using(object? obj, Action action)
{
Check.ArgumentNull(action);
try
{
action();
}
finally
{
(obj as IDisposable)?.Dispose();
}
}
///
/// Performs an and disposes all objects in the specified array that are .
///
/// An array of objects to be disposed.
/// The to be performed before the method is called. This is equivalent to the body of the statement.
public static void Using(object?[] objects, Action action)
{
Check.ArgumentNull(objects);
Check.ArgumentNull(action);
try
{
action();
}
finally
{
foreach (object? obj in objects)
{
(obj as IDisposable)?.Dispose();
}
}
}
///
/// Copies the contents of properties and fields of an to another of a different by comparing property and field names. A new instance of is created.
/// Values are only copied, if the property or field is of equivalent type. This includes conversion between mixed values (e.g. and ?), and between and numeric values. Differing types are attempted to convert (e.g. and ). If conversion fails, the default value of the destination type is used.
///
/// The type of the to copy the contents to.
/// The to copy the contents from.
///
/// The new instance of this method creates, with properties and fields copied from .
///
public static TDest ConvertObject(object obj) where TDest : class
{
return ConvertObject(obj, ConvertObjectOptions.None);
}
///
/// Copies the contents of properties and fields of an to another of a different by comparing property and field names. A new instance of is created.
/// Values are only copied, if the property or field is of equivalent type. This includes conversion between mixed values (e.g. and ?), and between and numeric values. Differing types are attempted to convert (e.g. and ). If conversion fails, the default value of the destination type is used.
///
/// The type of the to copy the contents to.
/// The to copy the contents from.
/// The flags that specify comparison and copy behavior.
///
/// The new instance of this method creates, with properties and fields copied from .
///
public static TDest ConvertObject(object obj, ConvertObjectOptions flags) where TDest : class
{
Check.ArgumentNull(obj);
TDest dest = Activator.CreateInstance();
ConvertObject(obj, dest, flags);
return dest;
}
///
/// Copies the contents of properties and fields of an to another of a different by comparing property and field names.
/// Values are only copied, if the property or field is of equivalent type. This includes conversion between mixed values (e.g. and ?), and between and numeric values. Differing types are attempted to convert (e.g. and ). If conversion fails, the default value of the destination type is used.
///
/// The type of the to copy the contents to.
/// The to copy the contents from.
/// The to copy the contents to.
public static void ConvertObject(object obj, TDest dest) where TDest : class
{
ConvertObject(obj, dest, ConvertObjectOptions.None);
}
///
/// Copies the contents of properties and fields of an to another of a different by comparing property and field names.
/// Values are only copied, if the property or field is of equivalent type. This includes conversion between mixed values (e.g. and ?), and between and numeric values. Differing types are attempted to convert (e.g. and ). If conversion fails, the default value of the destination type is used.
///
/// The type of the to copy the contents to.
/// The to copy the contents from.
/// The to copy the contents to.
/// The flags that specify comparison and copy behavior.
public static void ConvertObject(object obj, TDest dest, ConvertObjectOptions flags) where TDest : class
{
Check.ArgumentNull(obj);
Check.ArgumentNull(dest);
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public;
if (flags.HasFlag(ConvertObjectOptions.IgnoreCase)) bindingFlags |= BindingFlags.IgnoreCase;
if (flags.HasFlag(ConvertObjectOptions.NonPublic)) bindingFlags |= BindingFlags.NonPublic;
if (flags.HasFlag(ConvertObjectOptions.Static)) bindingFlags |= BindingFlags.Static;
if (!flags.HasFlag(ConvertObjectOptions.IgnoreProperties))
{
foreach (PropertyInfo sourceProperty in obj.GetType().GetProperties(bindingFlags))
{
if (dest.GetType().GetProperty(sourceProperty.Name, bindingFlags) is PropertyInfo destProperty && destProperty.SetMethod != null)
{
Process
(
sourceProperty.PropertyType,
destProperty.PropertyType,
() => sourceProperty.GetValue(obj),
value => destProperty.SetValue(dest, value)
);
}
if (flags.HasFlag(ConvertObjectOptions.PropertiesToFields) && dest.GetType().GetField(sourceProperty.Name, bindingFlags) is FieldInfo destField)
{
Process
(
sourceProperty.PropertyType,
destField.FieldType,
() => sourceProperty.GetValue(obj),
value => destField.SetValue(dest, value)
);
}
}
}
if (!flags.HasFlag(ConvertObjectOptions.IgnoreFields))
{
foreach (FieldInfo sourceField in obj.GetType().GetFields(bindingFlags))
{
if (dest.GetType().GetField(sourceField.Name, bindingFlags) is FieldInfo destField)
{
Process
(
sourceField.FieldType,
destField.FieldType,
() => sourceField.GetValue(obj),
value => destField.SetValue(dest, value)
);
}
if (flags.HasFlag(ConvertObjectOptions.FieldsToProperties) && dest.GetType().GetProperty(sourceField.Name, bindingFlags) is PropertyInfo destProperty && destProperty.SetMethod != null)
{
Process
(
sourceField.FieldType,
destProperty.PropertyType,
() => sourceField.GetValue(obj),
value => destProperty.SetValue(dest, value)
);
}
}
}
static void Process(Type sourceType, Type destType, Func