在c#中强制没有接口或抽象类的方法实现

时间:2014-05-12 23:16:52

标签: c# wpf class xaml

我正在创建一个模块化类型系统,一个应用程序(主机)将其他模块加载到一个通用UI界面,我还为此创建了一个API,供其他用户使用。

在某些情况下,API使用接口和抽象类来强制客户端使用API​​来实现特定方法。

我无法使用接口来处理所有内容,因为有些东西需要放在我自己的身体中,所以最终用户不需要自己实现所有不必要的事件和方法。 EG:所以我自己处理大小变化事件,然后将他的大小传递给他从名为SizeChanged实现的函数,他可以从大小变化处理他的程序。

抽象类是我真正想要使用的,但我不能,因为用户拥有的控件可能需要在XAML中指定的x:Name并且会收到错误:

  

类型...不能具有Name属性。没有默认构造函数的值类型和类型可以用作ResourceDictionary中的项目。

发生错误是因为需要创建它的实例: Inherited Window can not have a name?

我所知道的唯一可用解决方案是使用一个带有虚拟方法的常规类,这些方法可以被覆盖,并且可以正常工作,但它不会强制用户实现我需要的方法。

有什么我可以干净利落的,比如任何公开的可实现的东西吗?

这是结构:

API: IContext - > ContextControl - > (抽象方法)

模块DLL ContextControl(API) - > AppContextControl(覆盖方法)

Host Application pull的AppContextControl

我知道我可以告诉模块dev实现一个接口以及这个ContextControl来限制它们实现接口,但是编译器告诉它们会更专业。

在开发模块中,如果我不是从ContextControl继承而是实现IContext,那么我将失去所有默认的身体,并且模块开发人员必须实现更多。

干杯。

5 个答案:

答案 0 :(得分:1)

您所描述的两种方式 - 接口和抽象类 - (我确定您知道)建议的方式来执行您所描述的内容,至少,如果您想要在编译时强制执行。

我唯一知道的另一种方法是提供抛出NotImplementedException()的默认实现。不幸的是,这会给你一个运行时错误,但在编译时没有任何错误。

答案 1 :(得分:1)

这可能适用于您的具体情况,也可能不适用,但另一种可能的方法是使用策略模式。

不要让用户覆盖成员,而是让他们将包含该功能的参数传递给构造函数。这可以是代表,也可以是您为此目的创建的特定类。

答案 2 :(得分:0)

您是否可以使用继承和接口的组合?换句话说,为可以覆盖的方法创建virtual方法并使用接口来实现必须覆盖的方法?

答案 3 :(得分:0)

我最近遇到了类似的问题(dnt知道它会对你的情况有所帮助)。我必须在编译时而不是运行时生成错误,具有通用功能。我使用的方法是GenericsInterfaces的组合。例子是......

1) Enum Ex:C# String enums) 问题是设置东西,以便我不得不在整个项目中实现每个代码并强制构造函数。

public abstract class EnumEx<T> where T : EnumEx<T>
{
    private readonly string _displayValue;
    private readonly string _value;

    protected static ReadOnlyCollection<T> EnumExs;

    protected EnumEx(string displayValue, string value)
    {
        _displayValue = displayValue;
        _value = value;
    }

    public string DisplayValue
    {
        get { return _displayValue; }
    }

    public static T FromString(string option)
    {
        foreach (var enumEx in EnumExs.Where(enumEx => enumEx._value == option))
        {
            return enumEx;
        }
        Debug.WriteLine(string.Format("Exception in EnumEX FromString({0})", option));
        return null;
    }

    public override string ToString()
    {
        return _value ?? string.Empty;
    }
}

2)深层复制Generic method to create deep copy of all elements in a collection)+ 可编辑实施 IEditableObject超过自定义列表

public abstract class CloneAbleBase<T> : ObservableObjectEx, ICloneable, IEditableObject
    where T : DataBase<T>, new()
{
    protected T CurrentData = new T();
    private T _backupData;
    private bool _isInEditMode;

    public object Clone()
    {
        var dataObject = (CloneAbleBase<T>) MemberwiseClone();
        dataObject.CurrentData = CurrentData.Copy();
        return dataObject;
    }

    public void BeginEdit()
    {
        if (_isInEditMode) return;
        _backupData = CurrentData.Copy();
        _isInEditMode = true;
    }

    public void EndEdit()
    {
        if (!_isInEditMode) return;
        _backupData = default(T);
        _isInEditMode = false;
        RaisePropertyChanged(string.Empty);
    }

    public void CancelEdit()
    {
        if (!_isInEditMode) return;
        CurrentData = _backupData;
        RaisePropertyChanged(string.Empty);
    }
}

以类似的方式,您可以为Window或任何控件创建基类,在这里您需要功能某种通用功能。

答案 4 :(得分:0)

我可以在你非常具体的情况下看到一个替代方案是使用接口(正如你所说,它不会让你注入你自己的代码)然后继承最终用户提供和传入的实际类型作为一个接口,并在运行时注入您自己的代码

例如,您加载实现IMyPlugin定义的所有类

public interface IMyPlugin{
   void MyEndUserMethod();
   void YourMethod();
}

用户实现一个继承自它的类

public class UserClass:IMyPlugin{
....
}

您希望强制使用自己的代码而不是YourMethod,然后在运行时生成一个继承自UserClass的类,并在YourMethod中创建自己的代码并根据它调用(或不根据需要)。基于用户要提供实现的所有其他方法调用。

这对你来说有点多了,但它隐藏了最终用户的所有丑陋,只是迫使他实现了界面。为了减少丑陋,将该界面变为2接口

public interface IPlugin : IUserPlugin,ICreatorPlugin

向用户表明,虽然类必须实现IPlugin,但ICreatorPlugin中的任何内容都可以保留为空。

更好的解决方案是仅公开IUserPlugin并在您身边做更多工作(您的运行时类继承自用户类AND ICreatorPlugin,然后使用duck typing将其转换为IPlugin)。