簡體   English   中英

將對象從接口投射到其實現

[英]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的對象添加到列表中。

我知道我可以將asis轉換為實現的接口,但是在我的項目中,我每個實現都有大約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 返回IBaseInterfaceISpecialInterface將隱藏設置器,因為這些接口不繼承自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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM