【问题标题】:Multi-level inheritance with Implements on properties in VB.NET vs C#在 VB.NET 与 C# 中的属性上实现多级继承
【发布时间】:2011-03-05 11:25:25
【问题描述】:

假设我有 2 个这样定义的接口:

public interface ISkuItem
{
    public string SKU { get; set; }
}

public interface ICartItem : ISkuItem
{
    public int Quantity { get; set; }
    public bool IsDiscountable { get; set; }
}

当我在 C# 中实现接口时,VS 会生成以下模板代码:

public class CartItem : ICartItem
{

    #region ICartItem Members

    public int Quantity { get {...} set {...} }

    public bool IsDiscountable { get {...} set {...} }

    #endregion

    #region ISkuItem Members

    public string SKU { get {...} set {...} }

    #endregion
}

在 VB.NET 中,同样的类是这样构建的:

Public Class CartItem
    Implements ICartItem

    Public Property IsDiscountable As Boolean Implements ICartItem.IsDiscountable
        'GET SET'
    End Property

    Public Property Quantity As Integer Implements ICartItem.Quantity
        'GET SET'
    End Property

    Public Property SKU As String Implements ISkuItem.SKU
        'GET SET'
    End Property
End Class

VB.NET 明确要求您在实现的每个属性后添加 Implements IInterfaceName.PropertyName,而 C# 仅使用 regions 来指示哪些属性和方法属于接口。

有趣的是,在 VB.NET 中,在 SKU 属性上,我可以指定 Implements ISkuItem.SKUImplements ICartItem.SKU。虽然 VS 构建的模板默认为ISkuItem,但如果我愿意,我也可以指定ICartItem。奇怪的是,因为 C# 只使用regions 来屏蔽继承的属性,所以我似乎无法像在 VB.NET 中那样在 C# 中显式指定 SKU 的实现接口。

我的问题是:能够指定一个或另一个接口以在 VB.NET 中实现属性是否有任何重要性,如果是这样,有没有办法在 C# 中模仿此功能?此外,在实现属性时指定一个接口而不是另一个接口有什么影响?

【问题讨论】:

    标签: c# .net vb.net oop inheritance


    【解决方案1】:

    是的,这很重要,这称为Explicit and Implicit 接口实现。

    在 C# 中,您可以通过在方法名称前加上接口名称来做到这一点,如下所示:

    public class CartItem : ICartItem, ISkuItem
    {
    
        #region ICartItem Members
    
        public int Quantity { get {...} set {...} }
    
        public bool IsDiscountable { get {...} set {...} }
    
        #endregion
    
        #region ISkuItem Members
    
        public string ISkuItem.SKU { get {...} set {...} }  //like this
        public string ICartItem.SKU { get {...} set {...} } //like this
    
        #endregion
    }
    

    【讨论】:

    • 这是真实的并且信息丰富,但在 OP 的情况下,一个接口继承自另一个接口,这改变了事情。您发布的这段代码基于 OP 对两个接口的定义,不会编译。
    • 不过,这不是问题;如果CartItem 实现ICartItem,那么它也实现ISkuItem,无论你是否在类声明旁边写: ISkuItem。我的观点是 ISkuItem.SKUICartItem.SKU 是同一个东西,所以你不能在同一个类中定义它们(真的:“它”)两次。
    • 是的,我知道,但我只是想为您说明不同之处。
    【解决方案2】:

    是的,您可以在每个接口后面实现不同的功能。假设两个接口具有相同的签名。根据您将实现转换为哪个接口来控制执行哪个接口。

    ... C# 显式接口示例 ...

    public interface ITest1 { string Get(); }
    public interface ITest2 { string Get(); }
    // new is just to get rid of a compiler warning
    public interface ITest3 : ITest1, ITest2 { new string Get(); }
    public class MyTest : ITest1, ITest2
    {
        public string Get() { return "local"; }
        string ITest1.Get() { return "hello"; }
        string ITest2.Get() { return "world"; }
        string ITest3.Get() { return "hi"; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var mytest = new MyTest();
            // note that if mytest.Get() does not exist if all of the 
            // interfaces are explicit
            var v0 = mytest.Get(); //local
            var v1 = ((ITest1)mytest).Get(); //hello
            var v2 = ((ITest2)mytest).Get(); //world
            var v3 = ((ITest3)mytest).Get(); //hi
        }
    }
    

    ... VB.Net 中的类似代码...

    Module Module1
    
        Sub Main()
            Dim myinstance = New MyTest()
            Dim v0 = myinstance.DoWork() 'local
            'By the way... note that the following methods are called
            'by the interface signature and not the defind method name 
            'in the class
            Dim v1 = DirectCast(myinstance, ITest1).DoWork() 'hello
            Dim v2 = DirectCast(myinstance, ITest2).DoWork() 'world
            Dim v3 = DirectCast(myinstance, ITest3).DoWork() 'hi
        End Sub
    
    End Module
    
    Public Interface ITest1
        Function DoWork() As String
    End Interface
    Public Interface ITest2
        Function DoWork() As String
    End Interface
    Public Interface ITest3
        Inherits ITest1
        Inherits ITest2
    
        Shadows Function DoWork() As String
    End Interface
    Public Class MyTest
        Implements ITest3
        'Implements ITest1
        'Implements ITest2 
    
        Public Function DoWork() As String
            Return "local"
        End Function
    
        Private Function DoWork1() As String Implements ITest1.DoWork
            Return "hello"
        End Function
    
        Private Function DoWork2() As String Implements ITest2.DoWork
            Return "world"
        End Function
    
        Private Function DoWork3() As String Implements ITest3.DoWork
            Return "hi"
        End Function
    End Class
    

    【讨论】:

    • 这是真实的和信息丰富的,但在 OP 的情况下,一个接口从另一个接口继承,这会改变事情。
    • 哈哈,现在你刚刚制造了一个怪物。为什么要定义一个继承自两个具有单个冲突成员的接口的接口?这对我来说几乎毫无意义,除非您以这种方式定义它的唯一目的是拥有一个接口,该接口基本上可以保证它可以转换为两个 other 接口之一。但是,为什么是new/Shadows 成员?纯粹的疯狂。我不会忍受的。
    • @Dan,大声笑......我并不担心你是否会支持它。我的意思是更多地展示会发生什么。也不需要 new/Shadows...但是如果您不喜欢编译器警告,则需要它们。
    • VB cmets 在这个网站上的表现确实不好。
    • @Matthew:我的问题更多,“你为什么要在你的ITest3 接口上定义一个DoWork 成员?”就像,你有这两个接口,每个接口都有一个与另一个同名的方法。然后,您引入第三个接口,它不可能提供两者的功能(由于名称冲突),但它继承自两者。 此外,它再次定义了一个全新的同名成员。所以我认为这是一头披着传承服装的狼。在任何传统意义上,ITest3 根本不继承自 ITest1ITest2
    【解决方案3】:

    我认为这里的其他答案实际上有点离题。

    在您发布的示例中,一个接口继承另一个接口。这仅仅意味着它提供与其基础相同的成员,以及一些额外的成员。

    这些不是碰巧暴露同名成员的两个独立接口。 ICartItem.SKU ISkuItem.SKU 相同。 ICartItem 继承自 ISkuItem 仅仅意味着 ISkuItem 作为一个接口,代表了 ICartItem 提供的功能的一个子集。

    考虑这段代码:

    class CartItem : ICartItem
    {
        public int Quantity { get; set; }
        public bool IsDiscountable { get; set; }
    
        string ISkuItem.SKU
        {
            get { return "ISkuItem"; }
            set { throw new NotSupportedException(); }
        }
    
        string ICartItem.SKU
        {
            get { return "ICartItem"; }
            set { throw new NotSupportedException(); }
        }
    }
    

    这个类不会编译。在这种情况下,您不能明确定义 ICartItem.SKU,因为 ICartItem.SKU 只是 ISkuItem.SKU。没有要定义的“其他”SKU 属性。

    所以,直接回答你的问题:

    存在背后有什么重要性吗 能够指定一个接口或 另一个在 VB.NET?

    当它们是独立的、不相关的接口时
    正如其他人所指出的,您可以为不同接口的成员提供不同的实现,共享一个通用名称。

    但是当一个接口继承自另一个接口时
    没关系,因为它们是一样的。

    指定一个有什么效果 接口超过另一个时 实现属性?

    同样,如果它们是不相关的接口,它具有其他人已经讨论过的效果:为两个接口提供不同的实现。但是如果一个派生自另一个,则没有任何影响

    【讨论】:

    • 这是不同的点。合同ICartItem.SKU 不能显式,因为它不是ICartItem 的显式一部分
    • @Matthew:嗯,你也可以这样看;我的观点是,它们不是两个不同的成员,如果接口不相关,它们就会是。我认为在 VB.NET 中您可以指定 Implements ISkuItem.SKU Implements ICartItem.SKU 这一事实也使这一点更加清晰。
    • 顺便说一句...如果您创建CartItem 的实例,您将无法访问SKU 属性,除非您将其转换为ISkuItem 的接口(假设您添加@987654339 @ 到 ICartItem 这样这个例子就可以编译了。)
    • @Matthew:是的,你和我都知道这是显式实现接口背后的想法。但似乎你仍然错过了我的观点。 ISkuItem.SKUICartItem.SKU 相同。因此,您可以将 CartItem 转换为 ISkuItem ICartItemISkuItem.SKU 属性将是相同的。
    • VB.Net 在这方面的工作方式与 C# 相同。你不能使用Implements ICartItem.SKU,因为它没有定义
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-15
    • 2019-03-23
    • 1970-01-01
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多