如何使用继承类型覆盖接口属性

时间:2014-09-26 10:40:08

标签: c# interface

我很难描述标题中的问题,如果标题不是最清楚的话,对不起。

假设我有以下接口/类:

public interface IAction { /*...*/ }

public class WriteAction : IAction { /*...*/ }
public class ReadAction : IAction { /*...*/ }

现在,这些操作将在其他类中使用。 ReadWriteTest可以包含ReadActionWriteAction个对象,但ReadTest只能包含ReadAction个对象。
请注意,这些测试对象不仅仅是这个,我正在编写其他功能,因为它们与问题无关。

两个xxxTest类共享一个公共接口。所以接口和ReadWriteTest类的实现如下:

public interface ITest 
{ 
    List<IAction> Actions { get; set; }
}

public class ReadWriteTest : ITest
{
    public List<IAction> Actions { get; set; }
}

问题是对于ReadTest类,我想将此属性限制为仅包含ReadAction个元素。

我尝试实施的内容如下:

public class ReadTest : ITest
{
    public List<ReadAction> Actions { get; set; }
}

在我看来,这应该有效,因为每个ReadAction都固有地实现了IAction接口。

但是,编译器不喜欢它,并告诉我ReadTest类没有实现所有必需的IAction属性。

有没有办法在ReadTest类定义中限制此Actionlist的内容?

我可以通过创建一个自定义AddAction(IAction action)方法来解决这个问题,该方法根本不添加任何WriteAction个对象,但我希望能更优雅地解决这个问题。

这可能吗?

最终结果的更新

正如所回答的,可以通过向接口添加泛型类型来完成。这解决了我所描述的问题,但遗憾的是没有解决更大的问题。添加此generict类型意味着我现在无法将Test对象作为ITest来解决,因为我现在需要指定泛型参数,这对于任一测试都是不同的。

解决方案(或至少我的解决方案)是从Actions接口中删除ITest属性。 Actions属性仍然存在于类中 我没有在界面中定义list属性,而是在界面中添加了Run()方法。此方法将迭代Test类中的本地定义的Actions列表。

快速概述:

foreach(ITest myTest in myTests)
{
    myTest.Run()
}

ReadWriteTest的类实现:

public List<IAction> Actions {get; set;}

public void Run()
{
    foreach(IAction action in Actions) { /*...*/ }
}

ReadTest的类实现:

public List<ReadAction> Actions {get; set;}

public void Run()
{
    foreach(IAction action in Actions) //I could also declare it as a ReadAction. Either works.
    { /*...*/ }
}

2 个答案:

答案 0 :(得分:6)

您可以通过在ITest界面中使用通用类型来解决此问题:

public interface ITest<T> where T : IAction
{ 
    List<T> Actions { get; set; }
}

请注意强制传入类型为IAction的类型约束。这反过来使您的子类成为:

public class ReadWriteTest : ITest<IAction>
{
    public List<IAction> Actions { get; set; }
}

public class ReadTest : ITest<ReadAction>
{
    public List<ReadAction> Actions { get; set; }
}

答案 1 :(得分:2)

如果您可以接受更改界面以便无法通过属性直接设置操作,则可以使用out关键字使类变为协变。

这将允许您创建一个类型为ReadWriteTest的对象,并将其传递给接受ITest<IAction>类型参数的方法。

这是一个完整的可编辑控制台应用程序,用于演示:

using System;
using System.Collections.Generic;

namespace Demo
{
    // The basic IAction interface.

    public interface IAction
    {
        void Execute();
    }

    // Some sample implementations of IAction.

    public sealed class ReadAction: IAction
    {
        public void Execute()
        {
            Console.WriteLine("ReadAction");
        }
    }

    public sealed class ReadWriteAction: IAction
    {
        public void Execute()
        {
            Console.WriteLine("ReadWriteAction");
        }
    }

    public sealed class GenericAction: IAction
    {
        public void Execute()
        {
            Console.WriteLine("GenericAction");
        }
    }

    // The base ITest interface. 'out T' makes it covariant on T.

    public interface ITest<out T> where T: IAction
    {
        IEnumerable<T> Actions
        {
            get;
        }
    }

    // A ReadWriteTest class.

    public sealed class ReadWriteTest: ITest<ReadWriteAction>
    {
        public ReadWriteTest(IEnumerable<ReadWriteAction> actions)
        {
            _actions = actions;
        }

        public IEnumerable<ReadWriteAction> Actions
        {
            get
            {
                return _actions;
            }
        }

        private readonly IEnumerable<ReadWriteAction> _actions;
    }

    // A ReadTest class.

    public sealed class ReadTest: ITest<ReadAction>
    {
        public ReadTest(IEnumerable<ReadAction> actions)
        {
            _actions = actions;
        }

        public IEnumerable<ReadAction> Actions
        {
            get
            {
                return _actions;
            }
        }

        private readonly IEnumerable<ReadAction> _actions;
    }

    // A GenericTest class.

    public sealed class GenericTest: ITest<IAction>
    {
        public GenericTest(IEnumerable<IAction> actions)
        {
            _actions = actions;
        }

        public IEnumerable<IAction> Actions
        {
            get
            {
                return _actions;
            }
        }

        private readonly IEnumerable<IAction> _actions;
    }

    internal class Program
    {
        private static void Main()
        {
            // This demonstrates that we can pass the various concrete classes which have
            // different IAction types to a single test method which has a parameter of
            // type ITest<IAction>.

            var readActions = new[]
            {
                new ReadAction(),
                new ReadAction()
            };

            var test1 = new ReadTest(readActions);
            test(test1);

            var readWriteActions = new[]
            {
                new ReadWriteAction(),
                new ReadWriteAction(),
                new ReadWriteAction()
            };

            var test2 = new ReadWriteTest(readWriteActions);
            test(test2);

            var genericActions = new[]
            {
                new GenericAction(),
                new GenericAction(),
                new GenericAction(),
                new GenericAction()
            };

            var test3 = new GenericTest(genericActions);
            test(test3);
        }

        // A generic test method.

        private static void test(ITest<IAction> data)
        {
            foreach (var action in data.Actions)
            {
                action.Execute();
            }
        }
    }
}

如果您希望能够在创建对象后设置动作,则可以为每个具体类添加一个setter方法。

例如,您可以将ReadWriteTest类更改为:

public sealed class ReadWriteTest: ITest<ReadWriteAction>
{
    public void SetActions(IEnumerable<ReadWriteAction> actions)
    {
        _actions = actions;
    }

    public IEnumerable<ReadWriteAction> Actions
    {
        get
        {
            return _actions;
        }
    }

    private IEnumerable<ReadWriteAction> _actions = Enumerable.Empty<ReadWriteAction>();
}