[英]Cast object from an interface to its implementation
我有以下代碼:
public interface BaseInterface
{
int ID { get; }
}
public interface SpecialInterface1 : BaseInterface
{
int price { get; }
}
public interface SpecialInterface1 : BaseInterface
{
int xyz { get; }
}
public class Implementation1 : SpecialInterface
{
int price { get; }
int ID { get; internal set; }
}
public class Implementation2 : SpecialInterface
{
int xyz { get; }
int ID { get; internal set; }
}
現在,在管理類中,我想將實現BaseInterface
的對象添加到列表中。
我知道我可以將as
或is
轉換為實現的接口,但是在我的項目中,我每個實現都有大約10個特殊接口,因此我必須編寫一個很大的if語句。
public void Add(BaseInterface u, int id)
{
if (u is Implementation1)
{
((Implementation1)u).ID = id;
Units.Add(u);
}
if (u is Implementation2)
{
((Implementation2)u).ID = id;
Units.Add(u);
}
}
我的目標是在實現之外無法更改ID,我將僅提供DLL外部的接口,因此沒有人可以更改ID。
一種解決方案是添加一個額外的接口。 這消除了實現中的內部設置器。
internal interface IChangeID
{
void SetID(int id);
}
public interface IBaseInterface
{
int ID { get; }
}
public class Implementation : IBaseInterface,
IChangeID
{
public void SetID(int id) { ID = id; }
public int ID { get; private set; }
}
只有實際的實現才應該實現IChangeID
。 返回IBaseInterface
或ISpecialInterface
將隱藏設置器,因為這些接口不繼承自IChangeID
。
這會將您的添加更改為:
public void Add(BaseInterface u, int id)
{
((IChangeID)u).SetID(id);
Units.Add(u);
}
如果確實要返回具體類型,則不要返回接口。 您可以顯式實現給定的接口。 即使是具體的實現,這也將隱藏set方法。
public class Implementation : IBaseInterface,
IChangeID
{
void IChangeID.SetID(int id) { ID = id; }
public int ID { get; private set; }
}
var obj = new Implementation();
obj.SetID() // This WILL NOT Compile
雖然我最喜歡這個答案 ,
我建議將ID作為所有實現的構造函數的必需參數,然后使用工廠模式生成所需的任何實例。 這使得沒有ID集的任何實例在編譯時都會拋出異常,而不是在運行時降低異常的可能性。
這是一個簡單的示例,無需其他接口即可滿足您的需求。 如果您選擇,可以將我的答案與@Iqon的答案結合使用。
public interface IInterface
{
int ID { get; }
}
internal class InternalImplementation: IInterface {
public InternalImplementation(int ID) { this.ID = ID; }
public int ID { get; set; }
}
public class MyImplementationFactoryService {
public IInterface Create() {
int id = 1 // Or however you get your ID, possibly from a DB query?
return new InternalImplementation(id);
}
public IInterface Create(type|enum createtype) {
// return type based on typeof or enum
}
}
如果你不希望修改接口和實現,你可以使用C#7的模式匹配,而不鑄造訪問實現類型。 每個實現類型需要3行,但避免修改類:
public void Add(BaseInterface u, int id)
{
switch(u)
{
case Implementation1 u1:
u1.ID = id;
break;
case Implementation2 u1:
u1.ID = id;
break;
default :
throw new ArgumentException("Unexpected implementation!");
}
Units.Add(u);
}
明顯的缺點是,如果添加了新的實現,則必須修改代碼。
另一種選擇是使用dynamic
,失去類型安全性。 如果某些實現沒有setter,那么它將在運行時失敗(例如,因為它已被構造方法初始化取代)
public void Add(BaseInterface u, int id)
{
dynamic x =u;
x.ID=id;
Units.Add(x);
}
如果您想使用反射來設置屬性,下面的代碼可能會有所幫助
public interface IBaseInterface
{
int ID { get; }
}
public class Impl1 : IBaseInterface
{
public int ID { get; internal set; }
public int Price {get; set;}
}
public class Impl2 : IBaseInterface
{
public int ID { get { return 0;} }
public int Subscription {get; set;}
}
public class Program
{
public static void Main(string[] args)
{
IBaseInterface obj1 = new Impl1();
SetProperty(obj1, "ID", 100);
Console.WriteLine("Object1 Id is {0}", obj1.ID);
IBaseInterface obj2 = new Impl2();
SetProperty(obj2, "ID", 500);
Console.WriteLine("Object2 Id is {0}", obj2.ID);
}
private static void SetProperty(IBaseInterface obj, string propertyName, object id){
if(obj.GetType().GetProperty(propertyName).CanWrite) {
obj.GetType().GetProperty(propertyName).SetValue(obj, id);
Console.WriteLine("CanWrite property '{0}' : {1}" , propertyName, obj.GetType().GetProperty(propertyName).CanWrite);
}
}
}
輸出量
CanWrite屬性“ ID”:真
Object1 ID為100
Object2 ID為0
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.