C#で明示的な型キャストを使用して、基本クラスオブジェクトを派生クラス参照に割り当てることは可能ですか?
試してみましたが、実行時エラーが発生します。
C#で明示的な型キャストを使用して、基本クラスオブジェクトを派生クラス参照に割り当てることは可能ですか?
試してみましたが、実行時エラーが発生します。
いいえ。派生クラスへの参照は、実際には派生クラスのインスタンス(またはnull)を参照する必要があります。そうでなければ、どのように動作することを期待しますか?
例えば:
object o = new object();
string s = (string) o;
int i = s.Length; // What can this sensibly do?
基本型のインスタンスを派生型に変換できるようにする場合は、適切な派生型インスタンスを作成するメソッドを作成することをお勧めします。または、継承ツリーをもう一度見て、最初からこれを行う必要がないように再設計してみてください。
いいえ、派生クラス参照に割り当てることは、「基本クラスは派生クラスの完全な代替であり、派生クラスが実行できるすべてのことを実行できます」と言うようなものであるため、これは不可能です。これは、派生クラスが一般的に提供するため、当てはまりません。基本クラスよりも多くの機能(少なくとも、それが継承の背後にある考え方です)。
基本クラスオブジェクトをパラメーターとして、値をコピーして、派生クラスにコンストラクターを作成できます。
このようなもの:
public class Base {
public int Data;
public void DoStuff() {
// Do stuff with data
}
}
public class Derived : Base {
public int OtherData;
public Derived(Base b) {
this.Data = b.Data;
OtherData = 0; // default value
}
public void DoOtherStuff() {
// Do some other stuff
}
}
その場合、基本オブジェクトをコピーして、派生メンバーのデフォルト値を持つ完全に機能する派生クラスオブジェクトを取得します。このようにして、JonSkeetが指摘した問題を回避することもできます。
Base b = new Base();//base class
Derived d = new Derived();//derived class
b.DoStuff(); // OK
d.DoStuff(); // Also OK
b.DoOtherStuff(); // Won't work!
d.DoOtherStuff(); // OK
d = new Derived(b); // Copy construct a Derived with values of b
d.DoOtherStuff(); // Now works!
私はこの問題を抱えており、型パラメーターを取り、現在のオブジェクトをその型に変換するメソッドを追加することで解決しました。
public TA As<TA>() where TA : Base
{
var type = typeof (TA);
var instance = Activator.CreateInstance(type);
PropertyInfo[] properties = type.GetProperties();
foreach (var property in properties)
{
property.SetValue(instance, property.GetValue(this, null), null);
}
return (TA)instance;
}
つまり、次のようなコードで使用できます。
var base = new Base();
base.Data = 1;
var derived = base.As<Derived>();
Console.Write(derived.Data); // Would output 1
今日、私は同じ問題に直面し、JsonConvert
.
var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);
他の多くの人が答えているように、No.
基本型を派生型として使用する必要がある不幸な場合に、次のコードを使用します。はい、これは Liskov Substitution Principle (LSP) に違反しており、ほとんどの場合、継承よりも構成を優先します。Markus Knappen Johansson に感謝します。その元の回答はこれに基づいています。
基本クラスのこのコード:
public T As<T>()
{
var type = typeof(T);
var instance = Activator.CreateInstance(type);
if (type.BaseType != null)
{
var properties = type.BaseType.GetProperties();
foreach (var property in properties)
if (property.CanWrite)
property.SetValue(instance, property.GetValue(this, null), null);
}
return (T) instance;
}
許可:
derivedObject = baseObect.As<derivedType>()
反射を利用するので「高い」。それに応じて使用してください。
いいえ、それは不可能です。したがって、ランタイムエラーです。
ただし、派生クラスのインスタンスを基本クラスタイプの変数に割り当てることができます。
ここにいる誰もが言ったように、それは直接には不可能です。
私が好む、かなりクリーンな方法は、AutoMapperのようなオブジェクト マッパーを使用することです。
あるインスタンスから別のインスタンス (同じタイプである必要はありません) にプロパティを自動的にコピーするタスクを実行します。
@yboの答えを拡張する-基本クラスのインスタンスは実際には派生クラスのインスタンスではないため、それは不可能です。基本クラスのメンバーについてのみ知っており、派生クラスのメンバーについては何も知りません。
派生クラスのインスタンスを基本クラスのインスタンスにキャストできる理由は、派生クラスにはすでにそれらのメンバーが含まれているため、派生クラスは実際にはすでに基本クラスのインスタンスであるためです。逆は言えません。
基本クラスとして型指定された変数を派生クラスの型にキャストできます。ただし、必要に応じて、関連する実際のオブジェクトが正しいタイプであるかどうかを確認するために、実行時チェックが行われます。
一度作成されたオブジェクトのタイプは変更できません (少なくとも、同じサイズではない可能性があります)。ただし、インスタンスを変換して、2 番目のタイプの新しいインスタンスを作成することはできますが、変換コードを手動で記述する必要があります。
いいえ、できません。
ACBus が基本クラス Bus の派生クラスであるシナリオを考えてみましょう。ACBus には、ACState という名前のフィールドで動作する TurnOnAC や TurnOffAC などの機能があります。TurnOnAC は ACState をオンに設定し、TurnOffAC は ACState をオフに設定します。バスで TurnOnAC および TurnOffAC 機能を使用しようとしても意味がありません。
C# 9.0 では、これにレコードを使用することができます。すべてのフィールドをコピーするデフォルトのコピーコンストラクターがあります-すべてのフィールドでリフレクション/コンストラクターを使用する必要はありません。
public record BaseR
{
public string Prop1 { get; set; }
}
public record DerivedR : BaseR
{
public DerivedR(BaseR baseR) : base(baseR) { }
public string Prop2 { get; set; }
}
var baseR = new BaseR { Prop1 = "base prob" };
var derivedR = new DerivedR(baseR) { Prop2 = "new prop" };
関係ないかもしれませんが、ベースが与えられた派生オブジェクトでコードを実行できました。それは間違いなく私が望むよりもハックですが、動作します:
public static T Cast<T>(object obj)
{
return (T)obj;
}
...
//Invoke parent object's json function
MethodInfo castMethod = this.GetType().GetMethod("Cast").MakeGenericMethod(baseObj.GetType());
object castedObject = castMethod.Invoke(null, new object[] { baseObj });
MethodInfo jsonMethod = baseObj.GetType ().GetMethod ("ToJSON");
return (string)jsonMethod.Invoke (castedObject,null);
伝統的な意味ではありません... Jsonに変換してからオブジェクトに変換し、ブーム、完了です! 上記の Jesse は最初に回答を投稿しましたが、プロセスを非常に簡単にするこれらの拡張メソッドを使用しませんでした。いくつかの拡張メソッドを作成します。
public static string ConvertToJson<T>(this T obj)
{
return JsonConvert.SerializeObject(obj);
}
public static T ConvertToObject<T>(this string json)
{
if (string.IsNullOrEmpty(json))
{
return Activator.CreateInstance<T>();
}
return JsonConvert.DeserializeObject<T>(json);
}
ツールボックスに永遠に入れておけば、いつでもこれを行うことができます。
var derivedClass = baseClass.ConvertToJson().ConvertToObject<derivedClass>();
ああ、JSON の力。
このアプローチにはいくつかの落とし穴があります。キャストではなく、実際に新しいオブジェクトを作成しているため、問題になる場合とそうでない場合があります。プライベート フィールドは転送されず、パラメーターを持つコンストラクターは呼び出されないなどです。一部の子 json が割り当てられない可能性があります。ストリームは、JsonConvert によって本質的に処理されるわけではありません。ただし、クラスがプライベート フィールドとコンストラクターに依存していない場合、これはコンストラクターのマッピングと呼び出しを行わずにクラスからクラスへデータを移動する非常に効果的な方法です。これが、最初にキャストする主な理由です。
C# で明示的な型キャストを使用して、基本クラス オブジェクトを派生クラス参照に割り当てることは可能ですか?
明示的な変換だけでなく、暗黙的な変換も可能です。
C# 言語ではこのような変換演算子を使用できませんが、純粋な C# を使用してそれらを記述でき、機能します。暗黙的な変換演算子 ( ) を定義するクラスと演算子 ( Derived
) を使用するクラスはProgram
、別々のアセンブリで定義する必要があることに注意してください (たとえば、Derived
クラスは、クラスを含むことlibrary.dll
によって参照される にあります)。program.exe
Program
//In library.dll:
public class Base { }
public class Derived {
[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Implicit(Base a) {
return new Derived(a); //Write some Base -> Derived conversion code here
}
[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Explicit(Base a) {
return new Derived(a); //Write some Base -> Derived conversion code here
}
}
//In program.exe:
class Program {
static void Main(string[] args) {
Derived z = new Base(); //Visual Studio can show squiggles here, but it compiles just fine.
}
}
Visual Studio で Project Reference を使用してライブラリを参照すると、暗黙的な変換を使用すると VS に波線が表示されますが、問題なくコンパイルされます。を参照するだけの場合library.dll
、波線はありません。
いいえ、私が尋ねたこの質問を参照してください - Upcasting in .NET using generics
最良の方法は、クラスでデフォルトのコンストラクターを作成し、構築してからInitialise
メソッドを呼び出すことです