我可以在 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"属性。有没有办法将接口标记为动态,同时又是编译器的接口?

我可以在 C# 中制作动态接口吗?

不可以,您无法创建动态接口。接口是一组预定义的方法和属性,所有使用/实现接口的组件在运行时之前都知道这些方法和属性。如果组件实现接口,则保证接口的方法/属性可供组件用户使用。它还可以将一个组件替换为另一个组件,只要新组件实现相同的接口即可。

另一方面,动态对象没有任何预定义的内容 - 它的布局是在运行时根据可能随时间变化的条件生成的。如果函数返回动态对象,则多次调用它时,它可能会返回不同的动态对象(具有不同的属性)。当调用方调用此类函数时,无法保证返回对象的布局。这与接口相反。

正如@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"])访问。

可以将接口成员添加到内部字典中,以便可以通过索引器表示法查找所有成员 - 我个人觉得这种方法很脏。在这种情况下,我可能只是将动态字段分离到一个真正的字典中,而不是试图保持这些字段在某种程度上是接口一部分的错觉 - 它可能更类型化,但代码要干净得多。

相关文章: