Câu hỏi Không thể truyền từ Lớp cha mẹ sang Lớp trẻ em


Tôi đang cố gắng để đúc từ một lớp cha mẹ đến một lớp con nhưng tôi nhận được một InvalidCastException. Lớp con chỉ có một thuộc tính kiểu int. Có ai biết tôi cần làm gì không?


75
2018-06-12 19:39


gốc


Nó cũng tốt để biết rằng, bạn không thể sử dụng đúc rõ ràng cho cơ sở / lấy được các lớp liên quan. - Rzassar


Các câu trả lời:


Bạn không thể đúc động vật có vú thành chó - nó có thể là một con mèo.

Bạn không thể bỏ thức ăn vào bánh sandwich - nó có thể là phô mai.

Bạn không thể đúc một chiếc xe thành một chiếc Ferrari - nó có thể là một chiếc Honda, hoặc cụ thể hơn, Bạn không thể bỏ chiếc Ferrari 360 Modena vào chiếc Ferrari 360 Challange Stradale - có những phần khác nhau, mặc dù cả hai đều là Ferrari 360.


97
2018-05-12 15:44



Các rào chắn dễ hiểu, do đó không thể thực sự 'đúc' theo cách này. Nhưng nếu anh ta muốn một con chó có cùng màu mắt / trọng lượng / kiểu tóc / tuổi, vv như con mèo đang được giữ trong vật thể động vật có vú? Về cơ bản sao chép các thuộc tính chung. - FastAl
FastAl, đây chính là lý do tại sao chúng tôi có giao diện. Động vật có vú phải thực hiện động vật có vú và chứa màu mắt, trọng lượng, vv Bây giờ bạn có thể bỏ cả chó và mèo vào Động vật có vú. - Tom Deloford
Bạn có thể trycast động vật có vú vào con chó. Nếu đó là một con chó, đó là một con chó. Nếu không, nó sẽ trở thành null. "quá tải" chức năng có thể làm cho việc chuyển đổi không thể từ mèo sang chó có thể, nếu mèo có các chức năng quá tải cho phép điều này. Nhưng đó là công việc của bạn để xử lý việc mất dữ liệu và điều chỉnh dữ liệu không tồn tại. Giống như chuyển đổi móng vuốt để móng tay, đuổi chuỗi để đuổi theo bóng, vv ... - TamusJRoyce
Tôi nghĩ rằng các ví dụ là một chút cực đoan và chọn lọc và có lẽ diễn viên là một phím tắt cho một nhà xây dựng bản sao. Ví dụ, đang xây dựng một ferrari với các thuộc tính được định nghĩa trong xe đối tượng cơ sở. Hoặc, bắt đầu với một con người và tạo ra một Boy. Đúc và sử dụng trực tiếp? Đồng ý đó là một không-không. Nhưng nếu nó là một phần của nhà xây dựng hoặc một cái gì đó, có thể làm việc. Câu trả lời serialization dưới đây là một liên lạc tốt đẹp. - sirthomas
Bạn có thể đang tìm kiếm một kịch bản mà bạn cần một nhà xây dựng bản sao, một lớp chuyển đổi hoặc một mô hình adapter / wrapper được áp dụng? - Isaac Llopis


Một cách đơn giản để downcast trong C # là serialize parent và sau đó deserialize nó vào trong child.

 var serializedParent = JsonConvert.SerializeObject(parentInstance); 
 Child c  = JsonConvert.DeserializeObject<Child>(serializedParent);

Tôi có một ứng dụng giao diện điều khiển đơn giản, mô phỏng động vật thành chó, sử dụng hai dòng mã trên đây


87
2017-12-21 23:10



Làm tốt lắm! - Paul
Vâng, tôi sẽ ngần ngại gọi đây là một "downcast". - Kirk Woll
Thử nghiệm, có thể được sử dụng. - Marek Bar
Tôi thích khi ai đó nghĩ bên ngoài hộp và im lặng những người nói với OP rằng nó không thể được thực hiện (tiết kiệm cho một hoặc hai troll)! Cảm ơn sự hỗ trợ về điều này. Tôi đã cố gắng tìm ra điều này trong vài giờ gần đây :) - derekmx271
Đây là một giải pháp tuyệt vời. Tôi đã có một trường hợp mà lớp con của tôi chỉ là một wrapper cho một phụ huynh không có chức năng bổ sung. Tôi đã làm điều đó vì vậy tôi đã không phải nhập tham chiếu web vào ứng dụng của tôi vì nó nằm trong thư viện trợ giúp của tôi. Điều này cho phép tôi chuyển đổi cấp độ gốc thành lớp trình bao bọc của tôi. Cảm ơn bạn! - BrianVPS


Ví dụ mà tham chiếu lớp cơ sở của bạn đề cập đến không phải là một thể hiện của lớp con của bạn. Không có gì sai cả.

Cụ thể hơn:

Base derivedInstance = new Derived();
Base baseInstance = new Base();

Derived good = (Derived)derivedInstance; // OK
Derived fail = (Derived)baseInstance; // Throws InvalidCastException

Để diễn xuất thành công, thể hiện mà bạn đang downcasting phải là một cá thể của lớp mà bạn đang downcasting (hoặc ít nhất, lớp bạn đang downcasting phải nằm trong hệ thống phân cấp lớp của cá thể), nếu không thì cast sẽ thất bại.


53
2018-06-12 19:40



hoặc có khả năng Base otherDerived = new OtherDerived (); Derived otherFail = (Derived) otherDerived; - Blair Conrad
class Base {} class Derived: Base {} // Trong phương thức Main derived derivedInstance = new Derived (); Base baseInstance = new Base (); Có nguồn gốc tốt = (Có nguồn gốc) derivedInstance; Derived fail = (Có nguồn gốc) baseInstance; Điều này biên dịch mà không có bất kỳ lỗi nào trong .NET 3.5. Vấn đề bạn đang nói ở đâu? - pradeeptp
@pradeeptp: Tất nhiên nó được xây dựng. Ai nói gì về lỗi biên dịch? - Greg D


Có một số trường hợp khi một diễn viên như vậy sẽ có ý nghĩa.
Trường hợp của tôi, tôi đã nhận được một lớp BASE trên mạng, và tôi cần nhiều tính năng hơn cho nó. Vì vậy, bắt nó để xử lý nó trên mặt của tôi với tất cả các chuông và còi tôi muốn, và đúc các lớp BASE nhận được vào DERIVED một chỉ đơn giản là không phải là một lựa chọn (Ném InvalidCastException của khóa học)

Một thực tế suy nghĩ GIẢI PHÁP là tuyên bố một lớp trợ giúp EXTENSION mà không kế thừa lớp BASE thực sự, nhưng BAO GỒM CNTT là một thành viên.

public class BaseExtension
{
   Base baseInstance;

   public FakeDerived(Base b)
   {
      baseInstance = b;
   }

   //Helper methods and extensions to Base class added here
}

Nếu bạn có khớp nối lỏng lẻo và chỉ cần một vài tính năng bổ sung cho lớp cơ sở mà không cần CÓ THẬT KHÔNG có nhu cầu tuyệt đối về nguồn gốc, đó có thể là cách giải quyết nhanh chóng và đơn giản.


14
2017-10-27 15:30



Tôi có nghĩ rằng bạn có thể muốn BaseExtension ở đây ít nhất là triển khai IBase sao cho bạn có thể sử dụng nó trong các bối cảnh tương tự? Hay không quan trọng cho nhu cầu của bạn? - tobriand
một số lần bao gồm có thể là một sự thay thế thích hợp cho thừa kế - Vahid Ghadiri


Điều đó sẽ vi phạm các nguyên tắc hướng đối tượng. Tôi muốn nói một giải pháp thanh lịch ở đây và các nơi khác trong dự án là sử dụng một khung bản đồ đối tượng như AutoMapper để định cấu hình phép chiếu.

Đây là một cấu hình phức tạp hơn một chút so với việc cần thiết nhưng đủ linh hoạt cho hầu hết các trường hợp:

public class BaseToChildMappingProfile : Profile
{
    public override string ProfileName
    {
        get { return "BaseToChildMappingProfile"; }
    }

    protected override void Configure()
    {
        Mapper.CreateMap<BaseClass, ChildClassOne>();
        Mapper.CreateMap<BaseClass, ChildClassTwo>();
    }
}


public class AutoMapperConfiguration
{
    public static void Configure()
    {
        Mapper.Initialize(x =>
        {
            x.AddProfile<BaseToChildMappingProfile>();
        });
    }
}

Khi ứng dụng bắt đầu cuộc gọi AutoMapperConfiguration.Configure() và sau đó bạn có thể dự án như thế này:

ChildClassOne child = Mapper.Map<BaseClass, ChildClassOne>(baseClass);

Các thuộc tính được ánh xạ theo quy ước vì vậy nếu lớp được kế thừa các tên thuộc tính giống hệt nhau và ánh xạ được cấu hình tự động. Bạn có thể thêm các thuộc tính bổ sung bằng cách tinh chỉnh cấu hình. Xem tài liệu .


13
2018-04-01 23:47





Paul, bạn không hỏi 'Tôi có thể làm điều đó' được không - tôi giả sử bạn muốn biết làm sao để làm điều đó!

Chúng tôi đã phải làm điều này trên một dự án - có rất nhiều lớp chúng tôi thiết lập trong một thời trang chung một lần, sau đó khởi tạo các thuộc tính cụ thể cho các lớp dẫn xuất. Tôi sử dụng VB vì vậy mẫu của tôi là trong VB (khó khăn noogies), nhưng tôi lấy trộm các mẫu VB từ trang web này mà cũng có một phiên bản C # tốt hơn:

http://www.eggheadcafe.com/tutorials/aspnet/a4264125-fcb0-4757-9d78-ff541dfbcb56/net-reflection--copy-cl.aspx

Mã mẫu:

Imports System
Imports System.Collections.Generic
Imports System.Reflection
Imports System.Text
Imports System.Diagnostics

Module ClassUtils

    Public Sub CopyProperties(ByVal dst As Object, ByVal src As Object)
        Dim srcProperties() As PropertyInfo = src.GetType.GetProperties
        Dim dstType = dst.GetType

        If srcProperties Is Nothing Or dstType.GetProperties Is Nothing Then
            Return
        End If

        For Each srcProperty As PropertyInfo In srcProperties
            Dim dstProperty As PropertyInfo = dstType.GetProperty(srcProperty.Name)

            If dstProperty IsNot Nothing Then
                If dstProperty.PropertyType.IsAssignableFrom(srcProperty.PropertyType) = True Then
                    dstProperty.SetValue(dst, srcProperty.GetValue(src, Nothing), Nothing)
                End If
            End If
        Next
    End Sub
End Module


Module Module1
    Class base_class
        Dim _bval As Integer
        Public Property bval() As Integer
            Get
                Return _bval
            End Get
            Set(ByVal value As Integer)
                _bval = value
            End Set
        End Property
    End Class
    Class derived_class
        Inherits base_class
        Public _dval As Integer
        Public Property dval() As Integer
            Get
                Return _dval
            End Get
            Set(ByVal value As Integer)
                _dval = value
            End Set
        End Property
    End Class
    Sub Main()
        ' NARROWING CONVERSION TEST
        Dim b As New base_class
        b.bval = 10
        Dim d As derived_class
        'd = CType(b, derived_class) ' invalidcast exception 
        'd = DirectCast(b, derived_class) ' invalidcast exception
        'd = TryCast(b, derived_class) ' returns 'nothing' for c
        d = New derived_class
        CopyProperties(d, b)
        d.dval = 20
        Console.WriteLine(b.bval)
        Console.WriteLine(d.bval)
        Console.WriteLine(d.dval)
        Console.ReadLine()
    End Sub
End Module

Tất nhiên điều này không thực sự đúc. Nó tạo ra một đối tượng có nguồn gốc mới và sao chép các thuộc tính từ cha mẹ, để trống các thuộc tính con. Đó là tất cả những gì tôi cần làm và có vẻ như tất cả những gì bạn cần làm. Lưu ý rằng nó chỉ sao chép các thuộc tính, chứ không phải các thành viên (các biến công cộng) trong lớp (nhưng bạn có thể mở rộng nó để làm điều đó nếu bạn là vì sự xấu hổ phơi bày các thành viên công khai).

Đúc nói chung tạo ra 2 biến trỏ đến cùng một đối tượng (mini hướng dẫn ở đây, xin vui lòng không ném trường hợp ngoại lệ trường hợp góc với tôi). Có những chi tiết đáng kể cho điều này (tập thể dục cho người đọc)!

Tất nhiên tôi phải nói lý do tại sao các languague không cho phép bạn đi từ cơ sở để lấy ví dụ, nhưng làm theo cách khác. hãy tưởng tượng một trường hợp bạn có thể lấy một thể hiện của một hộp văn bản winforms (có nguồn gốc) và lưu nó trong một biến kiểu điều khiển Winforms. Tất nhiên, 'điều khiển' có thể di chuyển đối tượng xung quanh OK và bạn có thể xử lý tất cả những thứ 'điều khiển' về hộp văn bản (ví dụ: trên cùng, bên trái, thuộc tính .text). Không thể xem nội dung cụ thể của hộp văn bản (ví dụ: .multiline) mà không truyền biến loại 'kiểm soát' trỏ đến hộp văn bản trong bộ nhớ, nhưng nó vẫn còn trong bộ nhớ.

Bây giờ hãy tưởng tượng, bạn có một điều khiển, và bạn muốn trường hợp một biến kiểu textbox với nó. Bộ điều khiển trong bộ nhớ bị thiếu 'multiline' và các thứ textboxy khác. Nếu bạn cố gắng để tham khảo chúng, kiểm soát sẽ không kỳ diệu phát triển một tài sản đa cấp! Thuộc tính (xem nó như một biến thành viên ở đây, thực sự lưu trữ một giá trị - bởi vì có trong bộ nhớ của cá thể của hộp văn bản) phải hiện hữu. Vì bạn đang truyền, hãy nhớ, nó phải là cùng một đối tượng bạn đang trỏ đến. Do đó nó không phải là một hạn chế ngôn ngữ, nó là triết học không thể để trường hợp theo cách như vậy.


9
2018-05-12 15:40



Tôi biết đây là cách sau khi thực tế, nhưng bạn nên bao gồm "AndAlso dstProperty.CanWrite" để thử nghiệm "Nếu dstProperty IsNot Nothing" của bạn, để đảm bảo rằng nó không phải là một tài sản chỉ đọc. - JamesMLV
@ JamesMLV - cảm ơn bắt tốt. 'sau khi thực tế' - không giống như OP sẽ chấp nhận bất kỳ câu trả lời anyway :-( vì vậy không có thực tế để được sau khi Oh tốt. - FastAl


Tôi đã thấy hầu hết mọi người nói rõ ràng cha mẹ để con đúc là không thể, điều đó thực sự không đúng. Chúng ta hãy bắt đầu sửa đổi và thử chứng minh nó bằng các ví dụ.

Như chúng ta biết trong .net tất cả các castings có hai loại rộng.

  1. Đối với loại Giá trị
  2. Đối với loại tham chiếu (trong trường hợp của bạn là kiểu tham chiếu của nó)

Loại tham chiếu có thêm ba trường hợp tình huống chính trong đó bất kỳ trường hợp nào có thể nói dối.

Trẻ em thành cha mẹ (Truyền ngầm - Luôn thành công)

Trường hợp 1. Con cho bất kỳ phụ huynh trực tiếp hoặc gián tiếp

Employee e = new Employee();
Person p = (Person)e; //Allowed

Cha mẹ cho con (Trích dẫn rõ ràng - Có thể thành công)

Trường hợp 2. Biến mẹ giữ đối tượng cha mẹ (Không được phép)

Person p = new Person();  // p is true Person object
Employee e = (Employee)p; //Runtime err : InvalidCastException <-------- Yours issue

Trường hợp 3. Biến mẹ giữ đối tượng con (Luôn thành công)

Ghi chú: Vì các đối tượng có tính chất đa hình, có thể cho một biến của một kiểu lớp cha để giữ một kiểu con.

Person p = new Employee(); // p actually is Employee
Employee e = (Employee)p; // Casting allowed

Phần kết luận : Sau khi đọc trên tất cả, hy vọng nó sẽ có ý nghĩa bây giờ như cách cha mẹ để chuyển đổi trẻ em là có thể (Trường hợp 3).

Trả lời câu hỏi :

Câu trả lời của bạn là   trong trường hợp 2.Bạn có thể thấy việc đúc như vậy không được OOP cho phép và bạn đang cố gắng vi phạm quy tắc cơ bản của OOP. Luôn luôn chọn đường dẫn an toàn.

Hơn nữa, để tránh những tình huống đặc biệt như vậy. là / như các nhà khai thác sẽ giúp bạn đưa ra quyết định sáng suốt và cung cấp tính năng truyền an toàn.


5
2017-10-17 14:56





Ví dụ của đối tượng sẽ được tạo bằng cách sử dụng kiểu của lớp con, bạn không thể truyền một thể hiện kiểu gốc cho một kiểu con


3
2017-10-22 08:13





Kể từ C # 7.0, bạn có thể sử dụng từ khóa là để làm điều này :

Với những lớp được xác định:

class Base { /* Define base class */ }
class Derived : Base { /* Define derived class */ }

Sau đó bạn có thể làm một số thứ như:

void Funtion(Base b)
{
    if (b is Derived d)
    {
        /* Do something with d which is now a variable of type Derived */
    }
}

Mà sẽ tương đương với:

void Funtion(Base b)
{
    Defined d;
    if (b is Derived)
    {
        d = (Defined)b;
        /* Do something with d */
    }
}

Bây giờ bạn có thể gọi:

Function(new Derived()); // Will execute code defined in if

Cũng như

Function(new Base()); // Won't execute code defined in if

Bằng cách đó bạn có thể chắc chắn rằng downcast của bạn sẽ có hiệu lực và sẽ không ném một ngoại lệ!


2
2017-08-02 14:17