我有以下代码:
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中。
我知道我可以使用as
或is
将接口转换为实现,但在我的项目中,我有大约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。
答案 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
。返回IBaseInterface
或ISpecialInterface
将隐藏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