Câu trả lời:
Một cách đơn giản để downcast trong C # là tuần tự hóa phần tử gốc và sau đó giải mã hóa nó thành phần con.
var serializedParent = JsonConvert.SerializeObject(parentInstance);
Child c = JsonConvert.DeserializeObject<Child>(serializedParent);
Tôi có một ứng dụng bảng điều khiển đơn giản để biến động vật thành chó, sử dụng hai dòng mã trên ở đây
Bạn không thể biến động vật có vú thành chó - nó có thể là mèo.
Bạn không thể đúc thực phẩm thành bánh sandwich - đó có thể là bánh mì kẹp pho mát.
Bạn không thể đúc một chiếc xe thành một chiếc Ferrari - đó có thể là Honda, hoặc cụ thể hơn, Bạn không thể đúc một chiếc Ferrari 360 Modena thành Ferrari 360 Challange Stradale - có những bộ phận khác nhau, mặc dù cả hai đều là Ferrari 360.
Ví dụ mà tham chiếu lớp cơ sở của bạn đang đề 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ụ thể hơn:
Base derivedInstance = new Derived();
Base baseInstance = new Base();
Derived good = (Derived)derivedInstance; // OK
Derived fail = (Derived)baseInstance; // Throws InvalidCastException
Để truyền thành công, cá thể mà bạn đang dự báo phải là một phiên bản của lớp mà bạn đang dự báo xuống (hoặc ít nhất, lớp bạn đang dự báo phải nằm trong phân cấp lớp của cá thể đó), nếu không cast sẽ thất bại.
Có một số trường hợp khi một dàn diễn viên như vậy sẽ có ý nghĩa.
Trường hợp của tôi, tôi đang nhận được một lớp BASE qua mạng và tôi cần thêm các tính năng cho nó. Vì vậy, việc tạo ra nó để xử lý nó về phía tôi với tất cả các chuông và còi tôi muốn, và truyền lớp BASE đã nhận vào lớp DERIVED đơn giản không phải là một lựa chọn (Ném không hợp lệCastException của khóa học)
Một giải pháp thực tế có thể nghĩ ra là khai báo một lớp Trình trợ giúp MỞ RỘNG thực tế KHÔNG kế thừa lớp BASE, mà BAO GỒM nó như 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 THỰC SỰ cần dẫn xuất, đó có thể là một giải pháp nhanh chóng và đơn giản.
BaseExtension
ở đây của bạn ít nhất phải 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 đối với nhu cầu của bạn?
Tôi đã thấy hầu hết mọi người nói rằng không thể chọn cha mẹ cho con một cách rõ ràng , điều đó thực sự không đúng. Hãy bắt đầu sửa đổi và thử chứng minh điều đó bằng các ví dụ.
Như chúng ta biết trong .net, tất cả các vật đúc đều có hai loại lớn.
Loại tham chiếu có thêm ba trường hợp tình huống chính trong đó bất kỳ tình huống nào cũng có thể nói dối.
Trường hợp 1. Con với bất kỳ cha mẹ trực tiếp hoặc gián tiếp nào
Employee e = new Employee();
Person p = (Person)e; //Allowed
Trường hợp 2. Biến cha giữ đối tượng cha (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 cha giữ đối tượng con (Luôn thành công)
Lưu ý: Vì các đối tượng có tính chất đa hình, nên một biến của kiểu lớp cha có thể chứa kiểu con.
Person p = new Employee(); // p actually is Employee
Employee e = (Employee)p; // Casting allowed
Kết luận: Sau khi đọc xong tất cả, hy vọng bây giờ nó sẽ có ý nghĩa như cách chuyển đổi từ cha mẹ sang con cái có thể thực hiện được (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. Nơi bạn có thể thấy việc truyền như vậy không được OOP cho phép và bạn đang cố gắng vi phạm một trong những quy tắc cơ bản của OOP. Vì vậy, hãy luôn chọn con đường an toàn.
Hơn nữa, để tránh những trường hợp ngoại lệ như vậy .net đã khuyến nghị sử dụng là / như các toán tử sẽ giúp bạn đưa ra quyết định sáng suốt và cung cấp quá trình truyền an toàn.
Điều đó sẽ vi phạm các nguyên tắc hướng đối tượng. Tôi muốn nói rằng một giải pháp thanh lịch ở đây và ở những nơi khác trong dự án là sử dụng khung lập bản đồ đối tượng như AutoMapper để định cấu hình một 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 mứ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ể chiếu 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 thì các tên thuộc tính hoàn toàn giống 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 điều chỉnh cấu hình. Xem tài liệu .
Paul, bạn đã không hỏi 'Tôi có thể làm được không' - Tôi cho rằng bạn muốn biết cách làm điều đó!
Chúng tôi phải làm điều này trong một dự án - có nhiều lớp chúng tôi thiết lập theo kiểu chung chỉ 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 nên mẫu của tôi là VB (những người quen thuộc), nhưng tôi đã lấy trộm mẫu VB từ trang web này, trang này cũng có phiên bản C # tốt hơn:
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 đây không thực sự là diễn viên. Đó là tạo một đối tượng dẫn xuất mới và sao chép các thuộc tính từ cấp độ gốc, để 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ư đó là 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, không sao chép các thành viên (biến công khai) trong lớp (nhưng bạn có thể mở rộng nó để làm điều đó nếu bạn xấu hổ để lộ các thành viên công khai).
Nói chung, đúc tạo ra 2 biến trỏ đến cùng một đối tượng (hướng dẫn nhỏ ở đây, vui lòng không ném ngoại lệ trường hợp góc cho tôi). Có những phân nhánh đáng kể cho điều này (bài tập cho người đọc)!
Tất nhiên tôi phải nói tại sao sự uể oải không cho phép bạn đi từ cơ sở đến ví dụ dẫn xuất, nhưng lại làm theo cách khác. hãy tưởng tượng một trường hợp mà bạn có thể lấy một phiên bản của hộp văn bản winforms (dẫn xuất) và lưu trữ nó trong một biến kiểu Winforms control. Tất nhiên 'điều khiển' có thể di chuyển đối tượng xung quanh OK và bạn có thể giải quyết tất cả những điều 'điều khiển-y' về hộp văn bản (ví dụ: thuộc tính trên cùng, bên trái, .text). Không thể nhìn thấy nội dung cụ thể của hộp văn bản (ví dụ: .multiline) nếu không truyền biến kiểu 'điều khiển' trỏ đến hộp văn bản trong bộ nhớ, nhưng nó vẫ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 đặt một biến kiểu hộp văn bản vào nó. Điều khiển trong bộ nhớ bị thiếu 'multiline' và những thứ textboxy khác. Nếu bạn cố gắng tham chiếu chúng, điều khiển sẽ không phát triển một cách kỳ diệu thuộc tính nhiều dòng! Thuộc tính (nhìn nó giống như một biến thành viên ở đây, thực sự lưu trữ một giá trị - vì nó nằm trong bộ nhớ của cá thể hộp văn bản) phải tồn tại. Vì bạn đang truyền, hãy nhớ rằng nó phải là cùng một đối tượng mà bạn đang trỏ tới. Do đó, nó không phải là hạn chế về ngôn ngữ, về mặt triết học thì không thể xử lý theo cách như vậy.
Đối với tôi, chỉ cần sao chép tất cả các trường thuộc tính từ lớp cơ sở sang lớp cha như thế này là đủ:
using System.Reflection;
public static ChildClass Clone(BaseClass b)
{
ChildClass p = new ChildClass(...);
// Getting properties of base class
PropertyInfo[] properties = typeof(BaseClass).GetProperties();
// Copy all properties to parent class
foreach (PropertyInfo pi in properties)
{
if (pi.CanWrite)
pi.SetValue(p, pi.GetValue(b, null), null);
}
return p;
}
Một giải pháp phổ quát cho mọi đối tượng có thể được tìm thấy tại đây
Kể từ C # 7.0, bạn có thể sử dụng từ khóa is để thực hiện việc này:
Với những lớp được định nghĩa:
class Base { /* Define base class */ }
class Derived : Base { /* Define derived class */ }
Sau đó, bạn có thể làm một số việc như:
void Funtion(Base b)
{
if (b is Derived d)
{
/* Do something with d which is now a variable of type Derived */
}
}
Điều này 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ẽ hợp lệ và sẽ không có ngoại lệ!
Để ép kiểu, đối tượng thực tế phải có Kiểu bằng hoặc có nguồn gốc từ Kiểu mà bạn đang cố truyền tới ...
hoặc, nói theo cách ngược lại, Kiểu bạn đang cố truyền nó phải giống, hoặc một lớp cơ sở của kiểu thực tế của đối tượng.
nếu đối tượng thực tế của bạn thuộc loại Baseclass , thì bạn không thể truyền nó sang một loại lớp dẫn xuất ...
Một biến thể về cách tiếp cận tuần tự hóa cho những người sử dụng ServiceStack:
var child = baseObject.ConvertTo<ChildType>();
hoặc chi tiết hơn:
var child = baseObject.ToJson().FromJson<ChildType>();
Việc tuần tự hóa của ServiceStack có thể siêu nhanh và tất cả, nhưng rõ ràng, đây không phải là giải pháp cho các chuyển đổi lớn trong quá trình truyền có độ trễ thấp, cũng như cho các loại phức tạp. Điều đó có thể rõ ràng đối với bất kỳ ai sử dụng ServiceStack, nhưng tôi nghĩ rằng tôi sẽ làm rõ trước các bình luận.