[解決済み] あるオブジェクトのプロパティ値を、同じ型の別のオブジェクトに自動的に適用しますか?
2023-03-11 21:04:18
質問
T型の2つのオブジェクトA、Bがあるとき、各プロパティに対して明示的な代入を行わずに、Aのプロパティの値をBの同じプロパティに代入したいと思います。
このようなコードを保存したい。
b.Nombre = a.Nombre;
b.Descripcion = a.Descripcion;
b.Imagen = a.Imagen;
b.Activo = a.Activo;
といった具合に
a.ApplyProperties(b);
可能でしょうか?
どのように解決するのですか?
私は
MiscUtil
という
PropertyCopy
これは似たようなことをします - ただし、ターゲット型の新しいインスタンスを作成し、そこにプロパティをコピーします。
これは型が同じであることを必要としません - それはただ "ソース" 型から "ターゲット" 型にすべての読み取り可能なプロパティをコピーするだけです。もちろん、型が同じであれば、その方がうまくいく可能性は高くなります) これは浅いコピーです。
この回答の一番下にあるコードブロックでは、クラスの機能を拡張しました。あるインスタンスから別のインスタンスにコピーするために、それは単純な
PropertyInfo
これは式木を使用するよりも遅いですが、代替案は動的メソッドを書くことであり、私はそれにあまり熱中していません。もし、パフォーマンスがどうしても必要なら、私に知らせてください。このメソッドを使うには、次のようなことを書きます。
MyType instance1 = new MyType();
// Do stuff
MyType instance2 = new MyType();
// Do stuff
PropertyCopy.Copy(instance1, instance2);
(ここで
Copy
は型推論を使って呼び出される一般的なメソッドです)。
私はまだ完全な MiscUtil リリースを行う準備ができていませんが、コメントを含む更新されたコードがここにあります。SO エディターのためにそれらをリラップするつもりはありません - チャンク全体をコピーしてください。
(私がゼロから始めるのであれば、命名の面で API を少し再設計すると思いますが、既存のユーザーを壊したくはありません...)
#if DOTNET35
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace MiscUtil.Reflection
{
/// <summary>
/// Non-generic class allowing properties to be copied from one instance
/// to another existing instance of a potentially different type.
/// </summary>
public static class PropertyCopy
{
/// <summary>
/// Copies all public, readable properties from the source object to the
/// target. The target type does not have to have a parameterless constructor,
/// as no new instance needs to be created.
/// </summary>
/// <remarks>Only the properties of the source and target types themselves
/// are taken into account, regardless of the actual types of the arguments.</remarks>
/// <typeparam name="TSource">Type of the source</typeparam>
/// <typeparam name="TTarget">Type of the target</typeparam>
/// <param name="source">Source to copy properties from</param>
/// <param name="target">Target to copy properties to</param>
public static void Copy<TSource, TTarget>(TSource source, TTarget target)
where TSource : class
where TTarget : class
{
PropertyCopier<TSource, TTarget>.Copy(source, target);
}
}
/// <summary>
/// Generic class which copies to its target type from a source
/// type specified in the Copy method. The types are specified
/// separately to take advantage of type inference on generic
/// method arguments.
/// </summary>
public static class PropertyCopy<TTarget> where TTarget : class, new()
{
/// <summary>
/// Copies all readable properties from the source to a new instance
/// of TTarget.
/// </summary>
public static TTarget CopyFrom<TSource>(TSource source) where TSource : class
{
return PropertyCopier<TSource, TTarget>.Copy(source);
}
}
/// <summary>
/// Static class to efficiently store the compiled delegate which can
/// do the copying. We need a bit of work to ensure that exceptions are
/// appropriately propagated, as the exception is generated at type initialization
/// time, but we wish it to be thrown as an ArgumentException.
/// Note that this type we do not have a constructor constraint on TTarget, because
/// we only use the constructor when we use the form which creates a new instance.
/// </summary>
internal static class PropertyCopier<TSource, TTarget>
{
/// <summary>
/// Delegate to create a new instance of the target type given an instance of the
/// source type. This is a single delegate from an expression tree.
/// </summary>
private static readonly Func<TSource, TTarget> creator;
/// <summary>
/// List of properties to grab values from. The corresponding targetProperties
/// list contains the same properties in the target type. Unfortunately we can't
/// use expression trees to do this, because we basically need a sequence of statements.
/// We could build a DynamicMethod, but that's significantly more work :) Please mail
/// me if you really need this...
/// </summary>
private static readonly List<PropertyInfo> sourceProperties = new List<PropertyInfo>();
private static readonly List<PropertyInfo> targetProperties = new List<PropertyInfo>();
private static readonly Exception initializationException;
internal static TTarget Copy(TSource source)
{
if (initializationException != null)
{
throw initializationException;
}
if (source == null)
{
throw new ArgumentNullException("source");
}
return creator(source);
}
internal static void Copy(TSource source, TTarget target)
{
if (initializationException != null)
{
throw initializationException;
}
if (source == null)
{
throw new ArgumentNullException("source");
}
for (int i = 0; i < sourceProperties.Count; i++)
{
targetProperties[i].SetValue(target, sourceProperties[i].GetValue(source, null), null);
}
}
static PropertyCopier()
{
try
{
creator = BuildCreator();
initializationException = null;
}
catch (Exception e)
{
creator = null;
initializationException = e;
}
}
private static Func<TSource, TTarget> BuildCreator()
{
ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
var bindings = new List<MemberBinding>();
foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!sourceProperty.CanRead)
{
continue;
}
PropertyInfo targetProperty = typeof(TTarget).GetProperty(sourceProperty.Name);
if (targetProperty == null)
{
throw new ArgumentException("Property " + sourceProperty.Name + " is not present and accessible in " + typeof(TTarget).FullName);
}
if (!targetProperty.CanWrite)
{
throw new ArgumentException("Property " + sourceProperty.Name + " is not writable in " + typeof(TTarget).FullName);
}
if ((targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) != 0)
{
throw new ArgumentException("Property " + sourceProperty.Name + " is static in " + typeof(TTarget).FullName);
}
if (!targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType))
{
throw new ArgumentException("Property " + sourceProperty.Name + " has an incompatible type in " + typeof(TTarget).FullName);
}
bindings.Add(Expression.Bind(targetProperty, Expression.Property(sourceParameter, sourceProperty)));
sourceProperties.Add(sourceProperty);
targetProperties.Add(targetProperty);
}
Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings);
return Expression.Lambda<Func<TSource, TTarget>>(initializer, sourceParameter).Compile();
}
}
}
#endif
関連
-
[解決済み】文字列が有効な DateTime " format dd/MM/yyyy " として認識されなかった。
-
[解決済み] リフレクションを使用して文字列からプロパティ値を取得する
-
[解決済み] ディープクローンオブジェクト
-
[解決済み] 複数の例外を一度にキャッチする?
-
[解決済み] C#でベースコンストラクタを呼び出す
-
[解決済み] List<T>をオブジェクトのプロパティでソートする方法
-
[解決済み] 特定のプロパティに対するLINQのDistinct()
-
[解決済み] あるコンストラクタを別のコンストラクタから呼び出す
-
[解決済み] 汎用クラスやメソッドのメンバからTの型を取得する方法
-
[解決済み】大文字・小文字を区別しない「Contains(string)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] メンバー '<メンバー名>' にインスタンス参照でアクセスできない
-
[解決済み】C#におけるtypedefの等価性
-
[解決済み】"The ConnectionString property has not been initialized "を修正する方法
-
[解決済み] UnityでOnCollisionEnterが呼ばれない
-
[解決済み】MetadataException: 指定されたメタデータ・リソースをロードできない
-
[解決済み】Entity FrameworkからのSqlException - セッション内で他のスレッドが動作しているため、新しいトランザクションは許可されません。
-
[解決済み】値をNULLにすることはできません。パラメータ名:source
-
[解決済み】Linq 構文 - 複数列の選択
-
[解決済み】URLから画像をダウンロードする方法
-
[解決済み】WebResource.axdとは何ですか?