Có thể gán một đối tượng lớp cơ sở cho một tham chiếu lớp dẫn xuất với một typecast rõ ràng không?


88

Có thể gán một đối tượng lớp cơ sở cho một tham chiếu lớp dẫn xuất với một kiểu chữ rõ ràng trong C # không?

Tôi đã thử nó và nó tạo ra lỗi thời gian chạy.

Câu trả lời:


98

Không. Một tham chiếu đến một lớp dẫn xuất thực sự phải tham chiếu đến một thể hiện của lớp dẫn xuất (hoặc null). Nếu không, bạn sẽ mong đợi nó hoạt động như thế nào?

Ví dụ:

object o = new object();
string s = (string) o;
int i = s.Length; // What can this sensibly do?

Nếu bạn muốn có thể chuyển đổi một thể hiện của kiểu cơ sở sang kiểu dẫn xuất, tôi khuyên bạn nên viết một phương thức để tạo một thể hiện kiểu dẫn xuất thích hợp. Hoặc nhìn lại cây kế thừa của bạn và cố gắng thiết kế lại để bạn không cần phải làm điều này ngay từ đầu.


72
@Mike: Mã biên dịch tốt. Nó rơi qua tại thời gian thực hiện mặc dù :)
Jon Skeet

1
Sau đó, chính xác điều gì sẽ xảy ra khi chúng ta viết Base b = new Derived (); ? Nó sẽ tạo các đối tượng cho cả lớp cơ sở và lớp dẫn xuất?
Ashif Nataliya

3
@Akie: Không, nó tạo ra một đối tượng duy nhất cùng loại Derived, nhưng bạn có thể coi một Derivedtham chiếu như một Basetham chiếu.
Jon Skeet

Vì vậy, có sự khác biệt nào trong đối tượng kết quả cho hai câu lệnh này? Cơ sở b = new Cơ sở () và Cơ sở b = new Derived ()? lợi ích của việc sử dụng cái này hơn cái khác là gì?
Ashif Nataliya

4
@Akie: Có, một người tạo một thể hiện của Base, và người kia tạo một thể hiện của Derived. Nếu bạn gọi một phương thức ảo bđã được ghi đè Derived, bạn sẽ thấy Derivedhành vi nếu bạn có một phiên bản Derived. Nhưng nó không thực sự thích hợp để đi vào chi tiết trong chuỗi nhận xét Stack Overflow - bạn thực sự nên đọc một cuốn sách hoặc hướng dẫn C # hay, vì đây là nội dung khá cơ bản.
Jon Skeet

46

Không, điều đó là không thể vì việc gán nó cho một tham chiếu lớp dẫn xuất sẽ giống như nói "Lớp cơ sở là sự thay thế hoàn toàn có khả năng thay thế cho lớp dẫn xuất, nó có thể làm mọi thứ mà lớp dẫn xuất có thể làm", điều này không đúng vì các lớp dẫn xuất nói chung cung cấp nhiều chức năng hơn lớp cơ sở của chúng (ít nhất, đó là ý tưởng đằng sau sự kế thừa).

Bạn có thể viết một hàm tạo trong lớp dẫn xuất lấy một đối tượng lớp cơ sở làm tham số, sao chép các giá trị.

Một cái gì đó như thế này:

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
    }
}

Trong trường hợp đó, bạn sẽ sao chép đối tượng cơ sở và nhận một đối tượng lớp dẫn xuất đầy đủ chức năng với các giá trị mặc định cho các thành viên dẫn xuất. Bằng cách này, bạn cũng có thể tránh được vấn đề do Jon Skeet chỉ ra:

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!

23

Tôi đã gặp sự cố này và đã giải quyết nó bằng cách thêm một phương thức nhận tham số kiểu và chuyển đổi đối tượng hiện tại thành kiểu đó.

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;
}

Điều đó có nghĩa là bạn có thể sử dụng nó trong mã của bạn như sau:

var base = new Base();
base.Data = 1;
var derived = base.As<Derived>();
Console.Write(derived.Data); // Would output 1

Bạn nên sử dụng kiểu của lớp hiện tại (lớp cơ sở) để lấy và đặt các thuộc tính vì đó là những giá trị mà bạn muốn ánh xạ sang lớp dẫn xuất.
Bowofola

1
Nếu bạn có các thuộc tính không thể được ghi vào kiểu dẫn xuất, bạn có thể nên đổi thành: if (property.CanWrite) property.SetValue (instance, property.GetValue (this, null), null);
user3478586, 24/09/17

10

Như nhiều người khác đã trả lời, Không.

Tôi sử dụng mã sau trong những trường hợp không may khi tôi cần sử dụng kiểu cơ sở làm kiểu dẫn xuất. Có, đó là vi phạm Nguyên tắc thay thế Liskov (LSP) và có, hầu hết thời gian chúng tôi ưu tiên thành phần hơn là thừa kế. Đạo cụ cho Markus Knappen Johansson, người dựa trên câu trả lời ban đầu của nó.

Mã này trong lớp cơ sở:

    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;
    }

Cho phép:

    derivedObject = baseObect.As<derivedType>()

Vì nó sử dụng phản xạ, nó là "đắt tiền". Sử dụng phù hợp.


Tôi vừa thử điều này và nhận ra, nó có thể được cải thiện hơn nữa, bằng cách nạp chồng toán tử rõ ràng (và cả toán tử ngầm) .. nhưng - Trình biên dịch sẽ không cho phép điều đó: user-defined conversions to or from a base class are not allowed Tôi thấy lý do cho điều này, nhưng thất vọng, như nó đã có rất nhiều niềm vui nếu nó đã cho phép điều này ..
Henrik

@MEC: Tôi nhận thấy bạn đã bỏ phần `where T: MyBaseClass` và thêm if (type.BaseType != null)Statement liên quan đến Markus Knappen Johansson's A. Tại sao vậy? Điều đó có nghĩa là nó sẽ cho phép Nhập cuộc gọi không bắt nguồn từ MyBaseClass (hoặc bất kỳ thứ gì cho vấn đề đó). Tôi nhận ra rằng nó vẫn sẽ gây ra lỗi trình biên dịch nếu Được gán cho myDerivedObject, nhưng nếu nó chỉ được sử dụng như một Biểu thức, nó sẽ biên dịch và tại thời điểm chạy chỉ cần tạo một myDerivedObject mà không có bất kỳ dữ liệu nào được sao chép từ "myBaseObject". Tôi không thể tưởng tượng một trường hợp sử dụng cho điều đó.
Tom

@Tom, trả lời muộn, nhưng nghĩ rằng nó có thể vẫn hữu ích. Câu trả lời tốt nhất cho câu hỏi của bạn có lẽ sẽ là nói rằng tên "As" tốt hơn là "AsOrDefault". Về cơ bản, chúng ta có thể lấy kết quả này và so sánh nó với Mặc định như chúng ta làm khi sử dụng SingleOrDefault hoặc FirstOrDefault của Linq.
MEC

7

Không, không thể, do đó lỗi thời gian chạy của bạn.

Nhưng bạn có thể gán một thể hiện của lớp dẫn xuất cho một biến của kiểu lớp cơ sở.


7

Giải pháp với JsonConvert (thay vì typecast)

Hôm nay tôi gặp phải vấn đề tương tự và tôi đã tìm thấy một giải pháp đơn giản và nhanh chóng cho vấn đề bằng cách sử dụng JsonConvert.

var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);

Tôi đã trả lời điều này một lần nữa bên dưới với các phương pháp mở rộng. Vâng, đây là câu trả lời.
Patrick Knott

5

Như mọi người ở đây đã nói, điều đó không thể trực tiếp được.

Phương pháp tôi thích và khá rõ ràng là sử dụng một Object Mapper như AutoMapper .

Nó sẽ làm nhiệm vụ sao chép các thuộc tính từ thể hiện này sang thể hiện khác (Không nhất thiết phải cùng loại) một cách tự động.


3

Mở rộng trên câu trả lời của @ ybo - điều đó là không thể vì cá thể bạn có của lớp cơ sở thực sự không phải là một cá thể của lớp dẫn xuất. Nó chỉ biết về các thành viên của lớp cơ sở và không biết gì về những thành viên của lớp dẫn xuất.

Lý do mà bạn có thể truyền một thể hiện của lớp dẫn xuất sang một thể hiện của lớp cơ sở là vì lớp dẫn xuất thực sự đã là một thể hiện của lớp cơ sở, vì nó đã có các thành viên đó rồi. Điều ngược lại không thể nói.


3

Bạn có thể ép kiểu một biến được nhập là lớp cơ sở thành kiểu của lớp dẫn xuất; tuy nhiên, do cần thiết, điều này sẽ thực hiện kiểm tra thời gian chạy, để xem liệu đối tượng thực sự liên quan có thuộc loại chính xác hay không.

Sau khi được tạo, không thể thay đổi loại đối tượng (đặc biệt là, nó có thể không có cùng kích thước). Tuy nhiên, bạn có thể chuyển đổi một phiên bản, tạo một phiên bản mới của loại thứ hai - nhưng bạn cần phải viết mã chuyển đổi theo cách thủ công.


2

Không, no không thể.

Hãy xem xét một tình huống trong đó ACBus là một lớp dẫn xuất của Bus lớp cơ sở. ACBus có các tính năng như TurnOnAC và TurnOffAC hoạt động trên một trường có tên ACState. TurnOnAC đặt ACState thành bật và TurnOffAC đặt ACState thành tắt. Nếu bạn cố gắng sử dụng các tính năng TurnOnAC và TurnOffAC trên Xe buýt, nó không có ý nghĩa gì.


2
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");
    }
}

}

khi chúng ta tạo một đối tượng lớp con, đối tượng lớp cơ sở được khởi tạo tự động để biến tham chiếu lớp cơ sở có thể trỏ đến đối tượng lớp con.

nhưng không phải ngược lại vì một biến tham chiếu lớp con không thể trỏ đến đối tượng lớp cơ sở vì không có đối tượng lớp con nào được tạo.

và cũng lưu ý rằng biến tham chiếu lớp cơ sở chỉ có thể gọi thành viên lớp cơ sở.


2

Thực sự là có một cách để làm điều này. Hãy nghĩ về cách bạn có thể sử dụng Newtonsoft JSON để giải mã hóa một đối tượng từ json. Nó sẽ (hoặc ít nhất có thể) bỏ qua các phần tử bị thiếu và điền vào tất cả các phần tử mà nó biết.

Vì vậy, đây là cách tôi đã làm điều đó. Một mẫu mã nhỏ sẽ theo giải thích của tôi.

  1. Tạo một thể hiện của đối tượng của bạn từ lớp cơ sở và điền nó cho phù hợp.

  2. Sử dụng lớp "jsonconvert" của Newtonsoft json, tuần tự hóa đối tượng đó thành một chuỗi json.

  3. Bây giờ, hãy tạo đối tượng lớp con của bạn bằng cách deserializing với chuỗi json đã tạo ở bước 2. Thao tác này sẽ tạo một thể hiện của lớp con của bạn với tất cả các thuộc tính của lớp cơ sở.

Công việc này như một cái duyên vậy! Vậy .. khi nào điều này hữu ích? Một số người đã hỏi khi nào điều này có ý nghĩa và đề xuất thay đổi lược đồ của OP để phù hợp với thực tế là bạn không thể thực hiện điều này với kế thừa lớp (trong .Net).

Trong trường hợp của tôi, tôi có một lớp cài đặt chứa tất cả các cài đặt "cơ sở" cho một dịch vụ. Các dịch vụ cụ thể có nhiều tùy chọn hơn và chúng đến từ một bảng DB khác, vì vậy các lớp đó kế thừa lớp cơ sở. Tất cả họ đều có một loạt các tùy chọn khác nhau. Vì vậy, khi truy xuất dữ liệu cho một dịch vụ, dễ dàng hơn nhiều để ĐẦU TIÊN điền các giá trị bằng cách sử dụng một thể hiện của đối tượng cơ sở. Một phương pháp để thực hiện việc này với một truy vấn DB duy nhất. Ngay sau đó, tôi tạo đối tượng lớp con bằng phương thức được nêu ở trên. Sau đó, tôi thực hiện một truy vấn thứ hai và điền tất cả các giá trị động vào đối tượng lớp con.

Đầu ra cuối cùng là một lớp dẫn xuất với tất cả các tùy chọn được thiết lập. Việc lặp lại điều này cho các lớp con mới bổ sung chỉ mất một vài dòng mã. Nó đơn giản và nó sử dụng một gói rất được thử nghiệm và thử nghiệm (Newtonsoft) để làm cho điều kỳ diệu hoạt động.

Mã ví dụ này là vb.Net, nhưng bạn có thể dễ dàng chuyển đổi sang 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)

sử dụng C # và Newtonsoft.Json : var destObject = JsonConvert.DeserializeObject<DestinationType>(JsonConvert.SerializeObject(srcObject));. Tôi sẽ chỉ sử dụng điều này cho các bài kiểm tra đơn vị và "hack" phi sản xuất khác!
thinkOfaNumber

2

Bạn có thể sử dụng Phần mở rộng:

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));
    }

Trong mã:

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);

}

1

Có thể không bị loại bỏ, nhưng tôi đã có thể chạy mã trên một đối tượng dẫn xuất dựa trên cơ sở của nó. Nó chắc chắn là hack hơn tôi muốn, nhưng nó hoạt động:

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);

1

Bạn có thể làm điều này bằng cách sử dụng chung.

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; }
}

Bạn nhận được ba lợi ích khi sử dụng phương pháp này.

  1. Bạn không sao chép mã
  2. Bạn không sử dụng phản xạ (chậm)
  3. Tất cả các chuyển đổi của bạn đều ở một nơi

1

Tôi biết điều này đã cũ nhưng tôi đã sử dụng nó thành công trong một thời gian khá dài.

   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);
        }
    } 

1

Tôi đã kết hợp một số phần của các câu trả lời trước đó (nhờ những tác giả đó) và tạo thành một lớp tĩnh đơn giản với hai phương thức mà chúng tôi đang sử dụng.

Vâng, nó đơn giản, không nó không bao gồm tất cả các tình huống, vâng nó có thể được mở rộng và làm tốt hơn, không nó không hoàn hảo, vâng nó có thể được làm hiệu quả hơn, không, nó không phải là điều tuyệt vời nhất kể từ khi cắt lát bánh mì, vâng có trình lập bản đồ đối tượng gói nuget đầy đủ mạnh mẽ ngoài kia là cách tốt hơn cho việc sử dụng nhiều, v.v. vv, yada yada - nhưng nó hoạt động cho các nhu cầu cơ bản của chúng tôi :)

Và tất nhiên nó sẽ cố gắng ánh xạ các giá trị từ bất kỳ đối tượng nào sang bất kỳ đối tượng nào, có nguồn gốc hoặc không (tất nhiên chỉ có các thuộc tính chung được đặt tên giống nhau - bỏ qua phần còn lại).

SỬ DỤNG:

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);

LỚP TIỆN ÍCH TÌNH TRẠNG:

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);
            }
        });
    }
}

1

Bạn có thể sử dụng một phương thức khởi tạo sao chép ngay lập tức gọi phương thức khởi tạo cá thể hoặc nếu phương thức khởi tạo cá thể của bạn thực hiện nhiều hơn các phép gán, thì phương thức tạo bản sao sẽ gán các giá trị đến cho cá thể.

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; }
}

Đã tham chiếu Tài liệu C # của Microsoft trong Constructor cho ví dụ này đã gặp sự cố này trong quá khứ.


0

Một giải pháp khác là thêm phương thức mở rộng như sau:

 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;
            }
        }

sau đó có một hàm tạo trong mỗi lớp dẫn xuất chấp nhận lớp cơ sở:

  public class DerivedClass: BaseClass
    { 
        public DerivedClass(BaseClass baseModel)
        {
            this.CopyProperties(baseModel);
        }
    }

Nó cũng sẽ tùy chọn ghi đè các thuộc tính đích nếu đã được đặt (không phải null) hay không.


0

Có thể gán một đối tượng lớp cơ sở cho một tham chiếu lớp dẫn xuất với một kiểu chữ rõ ràng trong C # không?

Không chỉ có thể chuyển đổi rõ ràng mà còn có thể thực hiện được.

Ngôn ngữ C # không cho phép các toán tử chuyển đổi như vậy, nhưng bạn vẫn có thể viết chúng bằng C # thuần túy và chúng hoạt động. Lưu ý rằng lớp xác định toán tử chuyển đổi ngầm định ( Derived) và lớp sử dụng toán tử ( Program) phải được định nghĩa trong các tập hợp riêng biệt (ví dụ: Derivedlớp nằm trong a library.dllđược tham chiếu bởi program.exechứa Programlớp).

//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.
    }
}

Khi bạn tham chiếu thư viện bằng cách sử dụng Tham chiếu dự án trong Visual Studio, VS hiển thị các tiếng kêu khi bạn sử dụng chuyển đổi ngầm định, nhưng nó biên dịch tốt. Nếu bạn chỉ tham khảo library.dll, không có biến dạng.


Ma thuật đen gì đây?!? Ngoài ra, làm cách nào "Derived z = new Base ()" giúp tôi thực hiện "BaseCls baseObj; DerivedCls originObj; originObj = (DerivedCls) baseObj" (OP's Q)? Ngoài ra, System.Runtime.CompilerServices.SpecialNameThuộc tính làm gì? Tài liệu cho mọi phiên bản từ phiên bản sớm nhất có sẵn (2.0) đến "phiên bản hiện tại" (4.6? "Ai? Ai?") Không cho biết nó có tác dụng gì, nhưng nói rằng "Lớp SpecialNameAttribute hiện không được sử dụng trong .NET Khung, nhưng được dành để sử dụng trong tương lai. ". Xem: [link] ( msdn.microsoft.com/en-us/library/ms146064(v=vs.100).aspx ).
Tom,

> "Ma thuật đen gì đây?!?" Đó được gọi là .Net Framework (CLR, IL, BCL). Bộ tính năng của các ngôn ngữ IL, C # và VB không giống nhau. Có những tính năng trong VB mà C # không hỗ trợ. Có những tính năng trong IL mà C # không hỗ trợ. Có những hạn chế trong C # khá tùy ý và không tồn tại trong IL cơ bản (như where T : Delegatehoặc các thuộc tính được tham số hóa hay còn gọi là chỉ mục, v.v. v.v.).
Ark-kun

> "Ngoài ra," Derived z = new Base () "giúp tôi làm như thế nào" BaseCls baseObj; DerivedCls dẫn xuấtObj; originObj = (DerivedCls) baseObj "(OP's Q)?" Nó chỉ làm. Nó giải quyết câu hỏi của OP. Và bạn thậm chí không cần diễn viên rõ ràng.
Ark-kun

> what does System.Runtime.CompilerServices.SpecialName Attribute do?- Nó được sử dụng để đánh dấu các phương thức được tạo ra bởi một số cấu trúc tiện lợi đặc biệt của ngôn ngữ .Net cấp cao: trình truy cập thuộc tính, trình truy cập sự kiện, trình tạo, toán tử, trình chỉ mục, v.v. Trừ khi phương thức IL được đánh dấu bằng specialnamenó sẽ không được nhìn thấy là thuộc tính / sự kiện / phương thức khởi tạo và nó sẽ chỉ được công nhận là một phương thức bình thường. Việc đánh dấu thủ công các phương thức được đặt tên phù hợp với thuộc tính này chỉ là thực hiện thủ công một chút công việc của trình biên dịch.
Ark-kun

VB.Net có toán tử quyền lực. C # không. Làm thế nào bạn sẽ làm quá tải một toán tử nguồn trong C # để sử dụng trong VB.Net? Chỉ cần xác định một op_Exponentphương thức và đánh dấu nó bằng specialnamethuộc tính.
Ark-kun

0

Làm thế nào về:

public static T As<T>(this object obj)
    {
        return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
    }

0

Cách tốt nhất để thêm tất cả các thuộc tính cơ sở vào mục dẫn xuất là sử dụng phản chiếu trong cấu trúc chi phí. Hãy thử mã này mà không cần tạo phương thức hoặc phiên bản.

    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) { }
        }

    }

0

Tôi không đồng ý rằng điều đó là không thể. Bạn có thể làm như thế này:

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;
}

Sử dụng:

var sedan = new Sedan();
sedan.NumberOfDoors = 4;
var auto = ConvertAuto<Auto>(sedan);

var auto =vẫn thuộc loạisedan
bentecko

0

Đây là cách tôi giải quyết vấn đề này cho các trường. Bạn có thể thực hiện lặp lại tương tự thông qua các thuộc tính nếu bạn muốn. Bạn có thể muốn thực hiện một số kiểm tra cho nullvv nhưng đây là ý tưởng.

 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;
        }

0

Bạn chỉ có thể tuần tự hóa đối tượng cơ sở thành JSON và sau đó giải mã hóa nó thành đối tượng dẫn xuất.


0

Không theo cách hiểu truyền thống ... Chuyển đổi sang Json, sau đó sang đối tượng của bạn, và bùng nổ, xong! Jesse ở trên có câu trả lời được đăng trước, nhưng không sử dụng các phương pháp mở rộng này để làm cho quá trình dễ dàng hơn nhiều. Tạo một số phương pháp mở rộng:

    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);
    }

Đặt chúng vào hộp công cụ của bạn mãi mãi, sau đó bạn luôn có thể làm điều này:

var derivedClass = baseClass.ConvertToJson().ConvertToObject<derivedClass>();

Ah, sức mạnh của JSON.

Có một số vấn đề khó hiểu với cách tiếp cận này: Chúng tôi thực sự đang tạo một đối tượng mới, không phải đúc, điều này có thể có hoặc có thể không quan trọng. Các trường riêng sẽ không được chuyển, các hàm tạo có tham số sẽ không được gọi, v.v. Có thể một số json con sẽ không được gán. Các luồng không được JsonConvert xử lý bẩm sinh. Tuy nhiên, nếu lớp của chúng ta không dựa vào các trường riêng và các hàm tạo, thì đây là một phương pháp rất hiệu quả để di chuyển dữ liệu từ lớp này sang lớp khác mà không cần ánh xạ và gọi các hàm tạo, đó là lý do chính tại sao chúng ta muốn ép kiểu ngay từ đầu.


Điều này không làm những gì OP yêu cầu. Những gì bạn đang làm là xây dựng một đối tượng mới có kiểu đúng cho biến, sử dụng dữ liệu từ đối tượng gốc không đúng kiểu. Điều này có thể có hoặc có thể không hoạt động, nhưng dù bằng cách nào thì nó chắc chắn không gán một đối tượng của kiểu lớp cơ sở cho một biến của kiểu dẫn xuất.
Lasse V. Karlsen

Tôi đã trả lời câu hỏi: Có thể gán một đối tượng lớp cơ sở cho một tham chiếu lớp dẫn xuất với một typecast rõ ràng không? Bằng cách nói không. Tôi đang cung cấp một giải pháp thay thế hoàn toàn hoạt động và ít gây nhầm lẫn hơn thuốc chung. Như đã biểu thị nhiều lần ở trên, nó có thể gây ra sự cố khi gán cho thuộc tính lớp dẫn xuất từ ​​lớp cơ sở, tuy nhiên, đây chính xác là cách nó sẽ hoạt động (và thực hiện trong apis) nếu có thể. Chỉ vì câu trả lời của tôi có thể được sử dụng từ loại "sai" không có nghĩa là nó không thể được sử dụng cho loại "đúng". @ LasseV.Karlsen vui lòng rút lại đánh giá tiêu cực của bạn.
Patrick Knott

Không giống như hầu hết các câu trả lời ở đây là daisy chain JsonConverts, tôi cũng chỉ ra cách xử lý null.
Patrick Knott

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.