가 할당하는 기본 클래스의 객체를 파생된 클래스를 참조하여 명시적으로 배역?

StackOverflow https://stackoverflow.com/questions/729527

문제

가 할당하는 기본 클래스의 객체를 파생된 클래스를 참조하여 명시적으로 배역에서는 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.그것은(또는 적어도 할 수 있습니다)무시 누락된 요소들과 채우는 모든 요소는 그것에 대해 알아.

그래서 여기에 어떻게 했습니다.작은 코드 샘플을 따라 내가 설명입니다.

  1. 의 인스턴스를 만들의 개체에서의 기본 클래스고 채우는 서비스입니다.

  2. 를 사용하는"jsonconvert"등의 Newtonsoft json 직렬화하는 개체를 json 문자열입니다.

  3. 지금을 만들의 서브 클래스에 의해 개체 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; }
}

이 접근법을 사용하여 세 가지 이점을 얻습니다.

  1. 코드를 복제하지 않습니다
  2. 당신은 반사를 사용하지 않습니다 (느린다)
  3. 모든 전환은 한 곳에 있습니다

이전 답변의 일부를 결합하고 (저자 덕분에) 간단한 정적 클래스를 사용하여 사용하는 두 가지 방법으로 구성했습니다.

예, 간단합니다. 아니요, 모든 시나리오를 다루는 것은 아닙니다. 예, 확장되고 더 나아질 수 있습니다. 완벽하지는 않습니다. 예, 더 효율적일 수 있습니다. 얇게 썬 빵 이후로 가장 큰 것은 아닙니다. 예가 있습니다. 강력한 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 방법

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top