C#で明示的なタイプキャストを使用して、基本クラスオブジェクトを派生クラス参照に割り当てることは可能ですか?
試しましたが、実行時エラーが発生します。
C#で明示的なタイプキャストを使用して、基本クラスオブジェクトを派生クラス参照に割り当てることは可能ですか?
試しましたが、実行時エラーが発生します。
回答:
いいえ。派生クラスへの参照は、実際には派生クラスのインスタンス(またはnull)を参照する必要があります。そうでなければ、どのように動作すると思いますか?
例えば:
object o = new object();
string s = (string) o;
int i = s.Length; // What can this sensibly do?
基本型のインスタンスを派生型に変換できるようにする場合は、適切な派生型インスタンスを作成するメソッドを作成することをお勧めします。または、継承ツリーをもう一度見て、最初からこれを行う必要がないように再設計してみてください。
Derived
ますが、Derived
参照を参照として扱うことができBase
ます。
Base
作成し、もう一方はのインスタンスを作成しますDerived
。b
でオーバーライドされた仮想メソッドを呼び出すと、のインスタンスがある場合の動作Derived
が表示されDerived
ますDerived
。しかし、Stack Overflowのコメントスレッドで詳細を説明することは実際には適切ではありません。これはかなり基本的なことなので、優れたC#の本やチュートリアルを読む必要があります。
いいえ、派生クラス参照に割り当てることは、「基本クラスは派生クラスの完全な代替であり、派生クラスが実行できるすべてのことを実行できます」と言うようなものであるため、これは不可能です。これは、派生クラスが一般的に提供されているため、当てはまりません。基本クラスよりも多くの機能(少なくとも、それが継承の背後にある考え方です)。
基本クラスのオブジェクトをパラメーターとして、値をコピーして、派生クラスにコンストラクターを作成できます。
このようなもの:
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
他の多くの人が答えたように、いいえ。
派生型として基本型を使用する必要がある不幸な場合に、次のコードを使用します。はい、それはリスコフの置換原則(LSP)の違反であり、ほとんどの場合、継承よりも構成を優先します。これが基づいている元の答えのMarkusKnappenJohanssonへの小道具。
基本クラスのこのコード:
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>()
リフレクションを使用しているため、「高価」です。それに応じて使用してください。
user-defined conversions to or from a base class are not allowed
これの理由はわかりますが、がっかりしています、それはこの..許可しなかった場合、それは非常に楽しいようであったであろうと
if (type.BaseType != null)
Markus Knappen JohanssonのAに関連するステートメントを追加したことに気付きました。それはなぜですか?つまり、MyBaseClass(またはそれに関しては何でも)から派生していない呼び出しでタイプを許可することを意味します。myDerivedObjectに割り当てた場合でもコンパイラエラーが発生することはわかっていますが、式として使用した場合はコンパイルされ、実行時に「myBaseObject」からコピーされたデータなしでmyDerivedObjectが作成されます。そのユースケースを想像することはできません。
今日、私は同じ問題に直面し、を使用して問題の簡単で迅速な解決策を見つけましたJsonConvert
。
var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);
ここのみんなが言ったように、それは直接不可能です。
私が好み、かなりクリーンな方法は、AutoMapperのようなオブジェクトマッパーを使用することです。
あるインスタンスから別のインスタンス(必ずしも同じタイプである必要はありません)にプロパティを自動的にコピーするタスクを実行します。
いいえ、できません。
ACBusが基本クラスBusの派生クラスであるシナリオを考えてみます。ACBusには、ACStateという名前のフィールドで動作するTurnOnACやTurnOffACなどの機能があります。TurnOnACはACStateをオンに設定し、TurnOffACはACStateをオフに設定します。バスでTurnOnACおよびTurnOffAC機能を使用しようとしても、意味がありません。
class Program
{
static void Main(string[] args)
{
a a1 = new b();
a1.print();
}
}
class a
{
public a()
{
Console.WriteLine("base class object initiated");
}
public void print()
{
Console.WriteLine("base");
}
}
class b:a
{
public b()
{
Console.WriteLine("child class object");
}
public void print1()
{
Console.WriteLine("derived");
}
}
}
子クラスオブジェクトを作成すると、基本クラスオブジェクトが自動的に開始されるため、基本クラス参照変数は子クラスオブジェクトを指すことができます。
ただし、子クラスオブジェクトが作成されていないため、子クラス参照変数が基本クラスオブジェクトを指すことができないため、その逆はありません。
また、基本クラス参照変数は基本クラスメンバーのみを呼び出すことができることに注意してください。
実際にこれを行う方法があります。NewtonsoftJSONを使用してjsonからオブジェクトを逆シリアル化する方法を考えてください。欠落している要素を無視し、認識しているすべての要素にデータを入力します(または少なくとも可能です)。
これが私がそれをした方法です。小さなコードサンプルが私の説明に従います。
基本クラスからオブジェクトのインスタンスを作成し、それに応じてデータを入力します。
Newtonsoft jsonの「jsonconvert」クラスを使用して、そのオブジェクトをjson文字列にシリアル化します。
次に、手順2で作成したjson文字列を使用して逆シリアル化して、サブクラスオブジェクトを作成します。これにより、基本クラスのすべてのプロパティを含むサブクラスのインスタンスが作成されます。
これは魅力のように機能します!それで..これはいつ役に立ちますか?一部の人々は、これがいつ意味をなすかを尋ね、クラス継承(.Net)ではネイティブにこれを行うことができないという事実に対応するためにOPのスキーマを変更することを提案しました。
私の場合、サービスのすべての「基本」設定を含む設定クラスがあります。特定のサービスにはより多くのオプションがあり、それらは異なるDBテーブルから取得されるため、これらのクラスは基本クラスを継承します。それらはすべて異なるオプションのセットを持っています。したがって、サービスのデータを取得するときは、最初にベースオブジェクトのインスタンスを使用して値を入力する方がはるかに簡単です。単一のDBクエリでこれを行う1つの方法。その直後に、上記のメソッドを使用してサブクラスオブジェクトを作成します。次に、2番目のクエリを作成し、サブクラスオブジェクトにすべての動的な値を入力します。
最終的な出力は、すべてのオプションが設定された派生クラスです。追加の新しいサブクラスに対してこれを繰り返すと、ほんの数行のコードが必要になります。それは単純で、非常に試行錯誤されたパッケージ(Newtonsoft)を使用して魔法を機能させます。
このサンプルコードはvb.Netですが、c#に簡単に変換できます。
' First, create the base settings object.
Dim basePMSettngs As gtmaPayMethodSettings = gtmaPayments.getBasePayMethodSetting(payTypeId, account_id)
Dim basePMSettingsJson As String = JsonConvert.SerializeObject(basePMSettngs, Formatting.Indented)
' Create a pmSettings object of this specific type of payment and inherit from the base class object
Dim pmSettings As gtmaPayMethodAimACHSettings = JsonConvert.DeserializeObject(Of gtmaPayMethodAimACHSettings)(basePMSettingsJson)
var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));
。私はこれを単体テストやその他の非実稼働の「ハッキング」にのみ使用します。
エクステンションを使用できます:
public static void CopyOnlyEqualProperties<T>(this T objDest, object objSource) where T : class
{
foreach (PropertyInfo propInfo in typeof(T).GetProperties())
if (objSource.GetType().GetProperties().Any(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType()))
propInfo.SetValue(objDest, objSource.GetType().GetProperties().First(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType()).GetValue(objSource));
}
コード内:
public class BaseClass
{
public string test{ get; set;}
}
public Derived : BaseClass
{
//Some properies
}
public void CopyProps()
{
BaseClass baseCl =new BaseClass();
baseCl.test="Hello";
Derived drv=new Derived();
drv.CopyOnlyEqualProperties(baseCl);
//Should return Hello to the console now in derived class.
Console.WriteLine(drv.test);
}
関連性がないかもしれませんが、ベースが与えられた派生オブジェクトでコードを実行することができました。それは間違いなく私が望むよりもハッキーですが、それは機能します:
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);
ジェネリックを使用してこれを行うことができます。
public class BaseClass
{
public int A { get; set; }
public int B { get; set; }
private T ConvertTo<T>() where T : BaseClass, new()
{
return new T
{
A = A,
B = B
}
}
public DerivedClass1 ConvertToDerivedClass1()
{
return ConvertTo<DerivedClass1>();
}
public DerivedClass2 ConvertToDerivedClass2()
{
return ConvertTo<DerivedClass2>();
}
}
public class DerivedClass1 : BaseClass
{
public int C { get; set; }
}
public class DerivedClass2 : BaseClass
{
public int D { get; set; }
}
このアプローチを使用すると、3つの利点が得られます。
私はこれが古いことを知っていますが、私はこれをかなり長い間うまく使ってきました。
private void PopulateDerivedFromBase<TB,TD>(TB baseclass,TD derivedclass)
{
//get our baseclass properties
var bprops = baseclass.GetType().GetProperties();
foreach (var bprop in bprops)
{
//get the corresponding property in the derived class
var dprop = derivedclass.GetType().GetProperty(bprop.Name);
//if the derived property exists and it's writable, set the value
if (dprop != null && dprop.CanWrite)
dprop.SetValue(derivedclass,bprop.GetValue(baseclass, null),null);
}
}
以前の回答のいくつかの部分を組み合わせて(それらの作成者に感謝します)、使用している2つのメソッドを使用して単純な静的クラスをまとめました。
はい、それは単純です、いいえそれはすべてのシナリオをカバーしていません、はいそれは拡張されてより良くされる可能性があります、いいえそれは完璧ではありません、はいそれはおそらくより効率的にされる可能性があります、いいえそれはスライスされたパン以来の最高のものではありません、はいありますヘビーユースなどにはるかに適した、完全に堅牢なnugetパッケージオブジェクトマッパー、yada yada-しかし、基本的なニーズには対応しています:)
そしてもちろん、派生したかどうかに関係なく、任意のオブジェクトから任意のオブジェクトに値をマップしようとします(もちろん、同じ名前のパブリックプロパティのみ-残りは無視されます)。
使用法:
SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 };
// creates new object of type "RealPerson" and assigns any matching property
// values from the puppet object
// (this method requires that "RealPerson" have a parameterless constructor )
RealPerson person = ObjectMapper.MapToNewObject<RealPerson>(puppet);
// OR
// create the person object on our own
// (so RealPerson can have any constructor type that it wants)
SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 };
RealPerson person = new RealPerson("tall") {Name = "Steve"};
// maps and overwrites any matching property values from
// the puppet object to the person object so now our person's age will get set to 5 and
// the name "Steve" will get overwritten with "Elmo" in this example
ObjectMapper.MapToExistingObject(puppet, person);
静的ユーティリティクラス:
public static class ObjectMapper
{
// the target object is created on the fly and the target type
// must have a parameterless constructor (either compiler-generated or explicit)
public static Ttarget MapToNewObject<Ttarget>(object sourceobject) where Ttarget : new()
{
// create an instance of the target class
Ttarget targetobject = (Ttarget)Activator.CreateInstance(typeof(Ttarget));
// map the source properties to the target object
MapToExistingObject(sourceobject, targetobject);
return targetobject;
}
// the target object is created beforehand and passed in
public static void MapToExistingObject(object sourceobject, object targetobject)
{
// get the list of properties available in source class
var sourceproperties = sourceobject.GetType().GetProperties().ToList();
// loop through source object properties
sourceproperties.ForEach(sourceproperty => {
var targetProp = targetobject.GetType().GetProperty(sourceproperty.Name);
// check whether that property is present in target class and is writeable
if (targetProp != null && targetProp.CanWrite)
{
// if present get the value and map it
var value = sourceobject.GetType().GetProperty(sourceproperty.Name).GetValue(sourceobject, null);
targetobject.GetType().GetProperty(sourceproperty.Name).SetValue(targetobject, value, null);
}
});
}
}
インスタンスコンストラクターをすぐに呼び出すコピーコンストラクターを使用できます。または、インスタンスコンストラクターが割り当て以上のことを行う場合は、コピーコンストラクターに入力値をインスタンスに割り当てさせます。
class Person
{
// Copy constructor
public Person(Person previousPerson)
{
Name = previousPerson.Name;
Age = previousPerson.Age;
}
// Copy constructor calls the instance constructor.
public Person(Person previousPerson)
: this(previousPerson.Name, previousPerson.Age)
{
}
// Instance constructor.
public Person(string name, int age)
{
Name = name;
Age = age;
}
public int Age { get; set; }
public string Name { get; set; }
}
過去にこの問題が発生したこの例については、コンストラクターの下にあるMicrosoft C#ドキュメントを参照してください。
別の解決策は、次のような拡張メソッドを追加することです。
public static void CopyProperties(this object destinationObject, object sourceObject, bool overwriteAll = true)
{
try
{
if (sourceObject != null)
{
PropertyInfo[] sourceProps = sourceObject.GetType().GetProperties();
List<string> sourcePropNames = sourceProps.Select(p => p.Name).ToList();
foreach (PropertyInfo pi in destinationObject.GetType().GetProperties())
{
if (sourcePropNames.Contains(pi.Name))
{
PropertyInfo sourceProp = sourceProps.First(srcProp => srcProp.Name == pi.Name);
if (sourceProp.PropertyType == pi.PropertyType)
if (overwriteAll || pi.GetValue(destinationObject, null) == null)
{
pi.SetValue(destinationObject, sourceProp.GetValue(sourceObject, null), null);
}
}
}
}
}
catch (ApplicationException ex)
{
throw;
}
}
次に、基本クラスを受け入れる各派生クラスにコンストラクターを設定します。
public class DerivedClass: BaseClass
{
public DerivedClass(BaseClass baseModel)
{
this.CopyProperties(baseModel);
}
}
また、すでに設定されている(nullでない)場合は、オプションで宛先プロパティを上書きします。
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のプロジェクトリファレンスを使用してライブラリを参照する場合、暗黙的な変換を使用するとVSに波線が表示されますが、正常にコンパイルされます。を参照するだけlibrary.dll
では、波線はありません。
System.Runtime.CompilerServices.SpecialName
属性は何をしますか?利用可能な最も古いバージョン(2.0)から「現在のバージョン」(4.6?「誰か?誰か?」)までのすべてのバージョンのドキュメントには、その機能は記載されていませんが、「SpecialNameAttributeクラスは現在.NETでは使用されていません」と記載されています。フレームワークですが、将来の使用のために予約されています。」[リンク](msdn.microsoft.com/en-us/library/ms146064(v = vs.100).aspx)を参照してください。
where T : Delegate
ます(インデクサーなどのパラメーター化されたプロパティなど)。
what does System.Runtime.CompilerServices.SpecialName Attribute do?
-高レベルの.Net言語のいくつかの特別な便利な構造によって生成されたメソッドをマークするために使用されます:プロパティアクセサー、イベントアクセサー、コンストラクター、演算子、インデクサーなど。ILメソッドがマークされてspecialname
いない限り、表示されません。プロパティ/イベント/コンストラクタとして、通常の方法として認識されます。この属性を使用して適切な名前のメソッドを手動でマークすることは、コンパイラーの仕事を手動で行うだけです。
op_Exponent
メソッドを定義し、specialname
属性でマークするだけです。
派生アイテムにすべての基本プロパティを追加する最良の方法は、costructorでリフレクションを使用することです。メソッドやインスタンスを作成せずに、このコードを試してください。
public Derived(Base item) :base()
{
Type type = item.GetType();
System.Reflection.PropertyInfo[] properties = type.GetProperties();
foreach (var property in properties)
{
try
{
property.SetValue(this, property.GetValue(item, null), null);
}
catch (Exception) { }
}
}
私はそれが不可能であることに同意しません。あなたはこのようにそれを行うことができます:
public class Auto
{
public string Make {get; set;}
public string Model {get; set;}
}
public class Sedan : Auto
{
public int NumberOfDoors {get; set;}
}
public static T ConvertAuto<T>(Sedan sedan) where T : class
{
object auto = sedan;
return (T)loc;
}
使用法:
var sedan = new Sedan();
sedan.NumberOfDoors = 4;
var auto = ConvertAuto<Auto>(sedan);
var auto =
まだタイプですsedan
これは私がフィールドのためにこれを解決した方法です。必要に応じて、プロパティを介して同じ反復を行うことができます。あなたはnull
等のためにいくつかのチェックをしたいかもしれませんが、これは考えです。
public static DerivedClass ConvertFromBaseToDerived<BaseClass, DerivedClass>(BaseClass baseClass)
where BaseClass : class, new()
where DerivedClass : class, BaseClass, new()
{
DerivedClass derived = (DerivedClass)Activator.CreateInstance(typeof(DerivedClass));
derived.GetType().GetFields().ToList().ForEach(field =>
{
var base_ = baseClass.GetType().GetField(field.Name).GetValue(baseClass);
field.SetValue(derived, base_);
});
return derived;
}
従来の意味ではありません... 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によって本質的に処理されません。ただし、クラスがプライベートフィールドとコンストラクターに依存しない場合、これは、コンストラクターをマッピングして呼び出すことなく、クラス間でデータを移動する非常に効果的な方法です。これが、最初にキャストする主な理由です。
いいえ、私が尋ねたこの質問を参照してください-ジェネリックを使用した.NETでのアップキャスト
最良の方法は、クラスにデフォルトのコンストラクターを作成し、構築してからInitialise
メソッドを呼び出すことです。