26

クラスAと、クラスBを継承Aし、いくつかのフィールドで拡張するクラスがあります。

atypeのオブジェクトがある場合、オブジェクトに含まれるすべてのデータを含むtypeAのオブジェクトを作成するにはどうすればよいですか?bBa

試してみましたが、別の型オブジェクトa.MemberwiseClone()しか得られません。また、継承関係では反対のキャストしか許可されていないため、AキャストAすることはできません。B

これを行う正しい方法は何ですか?

4

10 に答える 10

11

コピーコンストラクターをAに追加してから、Aのインスタンスを取得してベースのコピーコンストラクターに渡す新しいコンストラクターをBに追加します。

于 2009-07-07T15:40:20.123 に答える
10

言語に自動的に組み込まれたこれを行う手段はありません...

1つのオプションは、クラスAを引数として取るコンストラクターをクラスBに追加することです。

次に、次のことができます。

B newB = new B(myA);

その場合、コンストラクターは必要に応じて関連データをコピーできます。

于 2009-07-07T15:39:26.740 に答える
5

これは、リフレクションを使用して実現できます。

利点:保守性。copy-constructor などを変更したり、プロパティを追加または削除したりする必要はありません。

短所:パフォーマンス。反射が遅い。ただし、平均サイズのクラスではまだミリ秒の話です。

拡張メソッドを使用して、サブクラスへのコピーをサポートするリフレクション ベースの浅いコピーの実装を次に示します。

public static TOut GetShallowCopyByReflection<TOut>(this Object objIn) 
{
    Type inputType = objIn.GetType();
    Type outputType = typeof(TOut);
    if (!outputType.Equals(inputType) && !outputType.IsSubclassOf(inputType)) throw new ArgumentException(String.Format("{0} is not a sublcass of {1}", outputType, inputType));
    PropertyInfo[] properties = inputType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    FieldInfo[] fields = inputType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    TOut objOut = (TOut)Activator.CreateInstance(typeof(TOut));
    foreach (PropertyInfo property in properties)
    {
        try
        {
            property.SetValue(objOut, property.GetValue(objIn, null), null);
        }
        catch (ArgumentException) { } // For Get-only-properties
    }
    foreach (FieldInfo field in fields)
    {
        field.SetValue(objOut, field.GetValue(objIn));
    }
    return objOut;
}

このメソッドは、すべてのプロパティ (プライベートとパブリック、およびすべてのフィールド) をコピーします。プロパティは参照によってコピーされるため、浅いコピーになります。

単体テスト:

[TestClass]
public class ExtensionTests {
    [TestMethod]
    public void GetShallowCloneByReflection_PropsAndFields()
    {
        var uri = new Uri("http://www.stackoverflow.com");
        var source = new TestClassParent();
        source.SomePublicString = "Pu";
        source.SomePrivateString = "Pr";
        source.SomeInternalString = "I";
        source.SomeIntField = 6;
        source.SomeList = new List<Uri>() { uri };

        var dest = source.GetShallowCopyByReflection<TestClassChild>();
        Assert.AreEqual("Pu", dest.SomePublicString);
        Assert.AreEqual("Pr", dest.SomePrivateString);
        Assert.AreEqual("I", dest.SomeInternalString);
        Assert.AreEqual(6, dest.SomeIntField);
        Assert.AreSame(source.SomeList, dest.SomeList);
        Assert.AreSame(uri, dest.SomeList[0]);            
    }
}

internal class TestClassParent
{
    public String SomePublicString { get; set; }
    internal String SomeInternalString { get; set; }
    internal String SomePrivateString { get; set; }
    public String SomeGetOnlyString { get { return "Get"; } }
    internal List<Uri> SomeList { get; set; }
    internal int SomeIntField;
}

internal class TestClassChild : TestClassParent {}
于 2014-01-13T11:47:30.150 に答える
4

ファクトリ メソッド パターンの使用:

    private abstract class A
    {
        public int P1 { get; set; }

        public abstract A CreateInstance();

        public virtual A Clone()
        {
            var instance = CreateInstance();
            instance.P1 = this.P1;
            return instance;
        }
    }

    private class B : A
    {
        public int P2 { get; set; }

        public override A CreateInstance()
        {
            return new B();
        }

        public override A Clone()
        {
            var result = (B) base.Clone();
            result.P2 = P2;
            return result;
        }
    }

    private static void Main(string[] args)
    {
        var b = new B() { P1 = 111, P2 = 222 };

        var c = b.Clone();
    }
于 2012-08-20T15:39:34.167 に答える
1

タイプAのオブジェクトを渡すことができるctorをBに作成し、Aフィールドをコピーして、必要に応じてBフィールドを設定します。

于 2009-07-07T15:38:36.397 に答える
0

基本クラスを取り込むクラスBでConvertメソッドを作成できます。

public ClassB Convert(ClassA a)
{
   ClassB b = new ClassB();
   // Set the properties
   return b;
}

ClassBのコンストラクターにClassAのオブジェクトを取り込むこともできます。

于 2009-07-07T15:40:03.943 に答える
0

いいえ、それはできません。これを実現する1つの方法は、タイプBのパラメーターを受け入れるクラスBにコンストラクターを追加し、データを手動で追加することです。

したがって、次のようなものがあります。

public class B
{
  public B(A a)
  {
    this.Foo = a.foo;
    this.Bar = a.bar;
    // add some B-specific data here
  }
}
于 2009-07-07T15:40:37.297 に答える
0

基本クラスで、以下の CreateObject 仮想メソッドを追加します...

    public virtual T CreateObject<T>()
    {
        if (typeof(T).IsSubclassOf(this.GetType()))
        {
            throw new InvalidCastException(this.GetType().ToString() + " does not inherit from " + typeof(T).ToString());
        }

        T ret = System.Activator.CreateInstance<T>();

        PropertyInfo[] propTo = ret.GetType().GetProperties();
        PropertyInfo[] propFrom = this.GetType().GetProperties();

        // for each property check whether this data item has an equivalent property
        // and copy over the property values as neccesary.
        foreach (PropertyInfo propT in propTo)
        {
            foreach (PropertyInfo propF in propFrom)
            {
                if (propT.Name == propF.Name)
                {
                    propF.SetValue(ret,propF.GetValue(this));
                    break;
                }
            }
        }

        return ret;
    }

次に、スーパークラスから実際のサブクラスオブジェクトを作成したいとします。

this.CreateObject<subclass>();

それはそれを行う必要があります!

于 2014-04-17T16:11:59.763 に答える