我创建了一个继承DynamicObject类的自定义类(DynamicItem),这适用于dynamic关键字。 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”属性未在接口上定义,并且是动态调用的。 “Path”属性在接口上定义。
此测试通过,一切正常,您可以预期。让我烦恼的是我必须从动态到界面进行演员表演。我希望能够做的只是使用界面:
[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”属性。有没有办法将接口标记为动态,同时作为编译器的接口?
答案 0 :(得分:2)
不,您无法创建动态界面。 interface是一组预定义的方法和属性,使用/实现接口的所有组件都可以在运行时之前知道。如果组件实现了接口,则可以保证接口的方法/属性可供组件的用户使用。它还可以将一个组件替换为另一个组件,只要新组件实现相同的接口即可。
另一方面,动态对象没有预先定义它 - 它的布局是在运行时根据可能随时间变化的条件生成的。如果函数返回动态对象,如果多次调用它,它可能会返回不同的动态对象(具有不同的属性)。当调用者调用这样的函数时,不能保证返回对象的布局。这与接口相反。
正如@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 ) );
}
}
}
我不确定这是否是最好的方法,但这会大致给出你想要实现的但却不太理想 - 真正的界面成员可以通过点表示法访问(即{{1但是动态成员只能通过索引符号(oItem.SomeItemName
或oItem[2]
)进行访问。
可以将接口成员添加到内部字典中,以便可以通过索引器符号查找所有成员 - 我个人认为这种方法很脏。在这种情况下,我可能只是将动态字段分离成一个真正的字典而不是试图保持这些字段在某种程度上是接口的一部分的错觉 - 它可能更多的打字但代码更清洁。 / p>