将对象从接口转换为其实现

时间:2017-09-06 14:39:10

标签: c# .net interface casting

我有以下代码:

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; }
}

现在,在Management类中,我想将实现BaseInterface的对象添加到List中。

我知道我可以使用asis将接口转换为实现,但在我的项目中,我有大约10个特殊接口,每个接口都有一个实现,所以我必须写一个如果陈述真的很大。

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。

4 个答案:

答案 0 :(得分:3)

解决方案是添加额外的接口。这消除了实现中的内部setter。

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将隐藏setter,因为这些接口不会从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

答案 1 :(得分:1)

虽然我最喜欢this SO answer

我建议将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
    }

}

答案 2 :(得分:0)

如果您不想想要修改接口和实现,您可以使用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);     
} 

答案 3 :(得分:0)

如果您想使用反射设置属性,下面的代码可能有帮助

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':True

Object1 Id为100

Object2 Id为0