我對 C# 泛型有疑問,我不確定最優雅的解決方案。我已經編程了一段時間,但對 C# 生態系統很陌生,所以不知道用于搜索的常用術語。
我正在嘗試重構代碼以減少現有的類的復制粘貼重復。使用一級泛型很容易解決,但我無法使用兩個泛型。
下面是一個非常簡化的示例。核心問題是 BaseProfile 不能使用與 DetailsA 或 DetailsB 相關的任何實作細節,因為它不知道型別。因此 UpdateDetailsId() 必須在 2 個派生類中復制,而不是讓單個 Profile 類處理它。請記住,這是一個玩具示例,僅用于表達關系。真正的類有幾十個欄位,但是我們在所討論的類中使用了一個公共子集,所以即使 DetailsA 和 DetailsB 看起來相同,假設我們都需要。
public abstract class BaseProfile<TypeOfPerson>
{
public TypeOfPerson Person { get; set; }
}
public class Profile1 : BaseProfile<PersonA>
{
public void UpdateDetailsId(int id)
{
this.Person.Details.Id = id;
}
}
public class Profile2 : BaseProfile<PersonB>
{
public void UpdateDetailsId(int id)
{
this.Person.Details.Id = id;
}
}
public class PersonA
{
public DetailsA Details { get; set; }
}
public class PersonB
{
public DetailsB Details { get; set; }
}
public class DetailsA
{
public int Id { get; set; }
}
public class DetailsB
{
public int Id { get; set; }
}
我可以添加介面,因為它指的是每種型別的所有相同欄位。但是,C# 不允許一個介面包含另一個介面并在實作中自動決議它,因為成員必須完全匹配,即我認為我可以將 IDetails 詳細資訊添加到 IPerson 介面但現在欄位需要輸入 IDetails 代替實作 IDetails 的 DetailsA。如果我這樣做,那么我將失去編譯器型別安全性,并且可能將錯誤的詳細資訊放在錯誤的人身上。
我已經成功完成了如下所示的公共/私有欄位對,但這僅在將值轉換為 DetailsA 時在運行時驗證和拋出。我更喜歡更安全的東西,但我不知道這是否是最佳選擇。此示例的目標是一個 Profile 類,處理多個 Person 類,每個類都有自己的具有 int Id 欄位的 Details 型別。
public class PersonA : IPerson
{
public IDetails Details
{
get { return _details; }
set { _details = (DetailsA)value; }
}
private DetailsA _details { get; set; }
}
uj5u.com熱心網友回復:
PersonA實作這一點的一種方法是以泛型方式定義to之間的型別關系DetailsA,并在 上指定第二個泛型型別BaseProfile。
Profile1 : BaseProfile<PersonA, DetailsA>
考慮以下代碼(注意我使用的是 Net6,所以我有所有這些可為空的參考型別運算子):
public abstract class BaseProfile<TPerson, TDetails>
where TDetails : IDetails, new()
where TPerson : PersonDetails<TDetails>, new()
{
public TPerson? Person { get; set; } = new TPerson();
public virtual void UpdateDetailsId(int id)
{
Person!.Details!.Id = id;
}
}
public class Profile1 : BaseProfile<PersonA, DetailsA>
{
}
public class Profile2 : BaseProfile<PersonB, DetailsB>
{
}
public abstract class PersonDetails<TDetails>
where TDetails : IDetails, new()
{
public virtual TDetails? Details { get; set; } = new TDetails();
}
public class PersonA : PersonDetails<DetailsA>
{
}
public class PersonB : PersonDetails<DetailsB>
{
}
public interface IDetails
{
int Id { get; set; }
}
public class DetailsA : IDetails
{
public int Id { get; set; }
public string? FirstName { get; set; }
}
public class DetailsB : IDetails
{
public int Id { get; set; }
public string? LastName { get; set; }
}
使用以下代碼段進行測驗
var profile1 = new Profile1();
var profile2 = new Profile2();
profile1.UpdateDetailsId(10);
profile2.UpdateDetailsId(12);
Console.WriteLine(profile1.Person!.Details!.Id);
Console.WriteLine(profile2.Person!.Details!.Id);
Console.WriteLine();
更新:
因為您在 Details 屬性 getter 和 setter 的代碼片段中包含了顯式轉換,所以我還想展示一個使用繼承這些泛型型別的具體型別的模式——然后演示隱式/顯式運算子用戶定義的轉換模式。
添加以下宣告:
public abstract class BaseProfile<TPerson>
where TPerson : PersonDetails<GenericDetails>, new()
{
public TPerson? Person { get; set; } = new TPerson();
public virtual void UpdateDetailsId(int id)
{
Person!.Details!.Id = id;
}
public static explicit operator Profile1(BaseProfile<TPerson> details)
{
var profile = new Profile1();
profile.Person!.Details = (GenericDetails)details.Person!.Details!;
return profile;
}
public static explicit operator Profile2(BaseProfile<TPerson> details)
{
var profile = new Profile2();
profile.Person!.Details = (GenericDetails)details.Person!.Details!;
return profile;
}
}
public class GenericProfile : BaseProfile<GenericPerson>
{
}
public abstract class GenericPersonDetails : PersonDetails<GenericDetails>
{
}
public class GenericPerson : GenericPersonDetails
{
}
public class GenericDetails : IDetails
{
public int Id { get; set; }
public static implicit operator DetailsA(GenericDetails details)
{
return new DetailsA() { Id = details.Id };
}
public static implicit operator DetailsB(GenericDetails details)
{
return new DetailsB() { Id = details.Id };
}
}
并且,更新測驗功能范圍:
var profile1 = new Profile1();
var profile2 = new Profile2();
var genericProfile = new GenericProfile();
profile1.UpdateDetailsId(10);
profile2.UpdateDetailsId(12);
genericProfile.UpdateDetailsId(20);
Console.WriteLine(profile1.Person!.Details!.Id);
Console.WriteLine(profile1.Person!.Details!.FirstName ?? "No First Name");
Console.WriteLine(profile2.Person!.Details!.Id);
Console.WriteLine(profile2.Person!.Details!.LastName ?? "No Last Name");
Console.WriteLine(genericProfile.Person!.Details!.Id);
Console.WriteLine(((Profile1)genericProfile).Person!.Details!.FirstName ?? "No First Name");
Console.WriteLine(((Profile2)genericProfile).Person!.Details!.LastName ?? "No Last Name");
Console.WriteLine();
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/477884.html
