我可以在 C# 中制作动态接口吗?
本文关键字:动态 接口 我可以 | 更新日期: 2023-09-27 18:30:29
我创建了一个继承DynamicObject类的自定义类(DynamicItem),它与动态关键字配合得很好。DynamicItem 还实现和接口,因为我知道某些属性将始终发生,我的测试如下所示:
[Test]
public void InterfaceTest()
{
//Assign
Item item = _db.GetItem(TargetPath);
dynamic d = new DynamicItem(item);
IDynamicItem i = d as IDynamicItem;
//Act
string result = d.Title;
string path = i.Path;
//Assert
Assert.AreEqual("awesome", result);
Assert.AreEqual(item.Path, path);
}
"Title"属性未在接口上定义,而是动态调用的。"路径"属性在接口上定义。
此测试通过,一切按预期工作。让我烦恼的是,我必须从动态到界面进行转换。我希望能够做的只是使用界面:
[Test]
public void InterfaceTest()
{
//Assign
Item item = _db.GetItem(TargetPath);
IDynamicItem d = new DynamicItem(item);
//Act
string result = d.Title;
string path = d.Path;
//Assert
Assert.AreEqual("awesome", result);
Assert.AreEqual(item.Path, path);
}
但是,如果我这样做,编译器会抱怨,因为它在接口上找不到"Title"属性。有没有办法将接口标记为动态,同时又是编译器的接口?
不可以,您无法创建动态接口。接口是一组预定义的方法和属性,所有使用/实现接口的组件在运行时之前都知道这些方法和属性。如果组件实现接口,则保证接口的方法/属性可供组件用户使用。它还可以将一个组件替换为另一个组件,只要新组件实现相同的接口即可。
另一方面,动态对象没有任何预定义的内容 - 它的布局是在运行时根据可能随时间变化的条件生成的。如果函数返回动态对象,则多次调用它时,它可能会返回不同的动态对象(具有不同的属性)。当调用方调用此类函数时,无法保证返回对象的布局。这与接口相反。
正如@SteveLillis评论的那样,您很少需要动态对象。在大多数情况下,您可以只使用Dictionary<string, object>
(或类似的东西),并获得一种更类型安全的方法,这种方法对其他人来说不那么容易混淆。在多年的编程中,我只需要使用 dynamic
关键字 2 或 3 次 - 在所有其他情况下,正确选择的数据结构就足够了 - 静态类型和类型安全(嗯,在某种程度上)。
下面是执行您正在尝试执行的操作的一个可能示例:
public interface IMyDynamicItem
{
string SomeItemName { get; set; }
object this[int nFieldIndex] { get; set; }
object this[string sFieldName] { get; set; }
IList<string> FieldNames { get; }
}
public class MyDynamicItem : IMyDynamicItem
{
private Dictionary<string, object> m_oFields =
new Dictionary<string, object> ();
public string SomeItemName { get; set; }
public object this[int nFieldIndex]
{
get
{
string sFieldName = FieldNames[nFieldIndex];
return ( m_oFields[sFieldName] );
}
set
{
string sFieldName = FieldNames[nFieldIndex];
m_oFields[sFieldName] = value;
}
}
public object this[string sFieldName]
{
get
{
return ( m_oFields[sFieldName] );
}
set
{
m_oFields[sFieldName] = value;
}
}
public IList<string> FieldNames
{
get
{
return ( new List<string> ( m_oFields.Keys ) );
}
}
}
我不确定这是否是最好的方法,但这会大致为您提供您想要实现的目标,但它不太理想 - 真正的接口成员可以通过点表示法(即 oItem.SomeItemName
)访问,但动态成员只能通过索引器表示法(oItem[2]
或oItem["SomeField"]
)访问。
可以将接口成员添加到内部字典中,以便可以通过索引器表示法查找所有成员 - 我个人觉得这种方法很脏。在这种情况下,我可能只是将动态字段分离到一个真正的字典中,而不是试图保持这些字段在某种程度上是接口一部分的错觉 - 它可能更类型化,但代码要干净得多。