가 할당하는 기본 클래스의 객체를 파생된 클래스를 참조하여 명시적으로 배역?
-
05-09-2019 - |
문제
가 할당하는 기본 클래스의 객체를 파생된 클래스를 참조하여 명시적으로 배역에서는 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
}
}
이 경우 기본 객체를 복사하고 파생 된 멤버에 대한 기본값이있는 완전히 기능적인 파생 클래스 객체를 가져옵니다. 이렇게하면 Jon Skeet이 지적한 문제를 피할 수 있습니다.
Base b = new Base();
Dervided d = new Derived();
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
다른 많은 사람들이 대답했듯이 아니요.
기본 유형을 파생 유형으로 사용해야 할 때 불행한 경우에 다음 코드를 사용합니다. 예, 그것은 Liskov 대체 원칙 (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의 답변 확장 - 기본 클래스의 인스턴스가 실제로 파생 클래스의 인스턴스가 아니기 때문에 불가능합니다. 기본 클래스의 구성원에 대해서만 알고 있으며 파생 클래스의 구성원에 대해 아무것도 모릅니다.
파생 클래스의 인스턴스를 기본 클래스의 인스턴스로 캐스팅 할 수있는 이유는 파생 클래스가 이미 멤버가 있기 때문에 이미 기본 클래스의 인스턴스이기 때문입니다. 반대는 말할 수 없습니다.
아니요, 불가능합니다.
ACBUS가 파생 된 기본 클래스 버스 인 시나리오를 고려하십시오. ACBUS에는 Turnonac 및 Turnoffac과 같은 기능이 있으며 Acstate라는 필드에서 작동합니다. Turnonac은 ACSTATE를 ON으로 설정하고 Turnoffac은 ACSTATE를 OFF로 설정합니다. 버스에서 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");
}
}
}
아동 클래스 객체를 만들 때 기본 클래스 객체가 자동 시작되므로 기본 클래스 참조 변수가 하위 클래스 객체를 가리킬 수 있습니다.
하위 클래스 참조 변수는 하위 클래스 객체가 만들어지지 않기 때문에 기본 클래스 객체를 가리킬 수 없기 때문에 그 반대도 마찬가지입니다.
또한 기본 클래스 참조 변수는 기본 클래스 멤버 만 호출 할 수 있습니다.
실제적으로 작업을 수행하는 방법입니다.에 대해 생각할 수 있는 방법을 사용하 Newtonsoft JSON 를 직렬화하는 개체에서 json.그것은(또는 적어도 할 수 있습니다)무시 누락된 요소들과 채우는 모든 요소는 그것에 대해 알아.
그래서 여기에 어떻게 했습니다.작은 코드 샘플을 따라 내가 설명입니다.
의 인스턴스를 만들의 개체에서의 기본 클래스고 채우는 서비스입니다.
를 사용하는"jsonconvert"등의 Newtonsoft json 직렬화하는 개체를 json 문자열입니다.
지금을 만들의 서브 클래스에 의해 개체 deserialize json 문자열에서 만든 단계 2.이 것이 인스턴스를 만들의 서브 클래스의 모든 속성을 기본 클래스입니다.
이 작업은 매우 간단합니다.그래서.이것이 유용한가?어떤 사람들은 때 묻 이해와 제안을 변경하는 영업 이익의 스키마를 수용할 수 있다는 사실 없는 기본적으로 이 클래스를 상속(습니다.Net).
내 경우에는,내가 설정 등을 포함하는"기본"설정한 서비스입니다.특정 서비스를 더 많은 옵션이고 그들과 다른 DB 테이블,그래서 해당 클래스를 상속 기본 클래스입니다.그들은 모두 다른 옵션이 있습니다.그래서 데이터를 검색할 때 서비스를 위해,그것은 훨씬 쉽게 먼저 채우는 값의 인스턴스를 사용하여 기본 개체입니다.방법 중 하나 이렇게 하나의 DB query.오른쪽 그 후,나는 만들기 서브 클래스의 객체를 사용하는 방법을 설명한다.나는 다음 두 번째는 쿼리를 채우는 모든 동적 가치에 하위 클래스 개체입니다.
최종 출력은 파생 등 모든 옵션을 설정 합니다.반복되 이에 대한 추가적인 새로운 서브 클래스는 단 몇 줄의 코드입니다.그것은 간단하고 그것을 사용하여 매우 시도하고 테스트 패키지는(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)
관련이 없을 수도 있지만 기본이 주어진 파생 된 객체에서 코드를 실행할 수있었습니다. 내가 원하는 것보다 확실히 해킹되지만 작동합니다.
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 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);
}
나는 이것이 늙었다는 것을 알고 있지만 나는 이것을 꽤 오랫동안 성공적으로 사용했습니다.
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);
}
}
jsonconvert (typecast 대신)가있는 솔루션
오늘 나는 같은 문제에 직면했고 간단하고 문제에 대한 빠른 해결책 사용 JsonConvert
.
var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);
또 다른 솔루션은 다음과 같은 확장 방법을 추가하는 것입니다.
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
클래스는 a에 있습니다 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에서 프로젝트 참조를 사용하여 라이브러리를 참조하면 암시 적 변환을 사용할 때 Squiggles를 표시하지만 잘 컴파일합니다. 당신이 그냥 참조하면 library.dll
, squiggles가 없습니다.
Generic을 사용 하여이 작업을 수행 할 수 있습니다.
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; }
}
이 접근법을 사용하여 세 가지 이점을 얻습니다.
- 코드를 복제하지 않습니다
- 당신은 반사를 사용하지 않습니다 (느린다)
- 모든 전환은 한 곳에 있습니다
이전 답변의 일부를 결합하고 (저자 덕분에) 간단한 정적 클래스를 사용하여 사용하는 두 가지 방법으로 구성했습니다.
예, 간단합니다. 아니요, 모든 시나리오를 다루는 것은 아닙니다. 예, 확장되고 더 나아질 수 있습니다. 완벽하지는 않습니다. 예, 더 효율적일 수 있습니다. 얇게 썬 빵 이후로 가장 큰 것은 아닙니다. 예가 있습니다. 강력한 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);
}
});
}
}
어때요 :
public static T As<T>(this object obj)
{
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
}
파생 항목에 모든 기본 속성을 추가하는 가장 좋은 방법은 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);
아니요, 내가 요청한이 질문을 참조하십시오. 제네릭을 사용하여 .NET에서 업 캐스트
가장 좋은 방법은 클래스에서 기본 생성자를 만들고 구성하고 호출하는 것입니다. Initialise
방법