我正在创建一个模块化类型系统,一个应用程序(主机)将其他模块加载到一个通用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,那么我将失去所有默认的身体,并且模块开发人员必须实现更多。
干杯。
答案 0 :(得分:1)
您所描述的两种方式 - 接口和抽象类 - (我确定您知道)建议的方式来执行您所描述的内容,至少,如果您想要在编译时强制执行。
我唯一知道的另一种方法是提供抛出NotImplementedException()
的默认实现。不幸的是,这会给你一个运行时错误,但在编译时没有任何错误。
答案 1 :(得分:1)
这可能适用于您的具体情况,也可能不适用,但另一种可能的方法是使用策略模式。
不要让用户覆盖成员,而是让他们将包含该功能的参数传递给构造函数。这可以是代表,也可以是您为此目的创建的特定类。
答案 2 :(得分:0)
您是否可以使用继承和接口的组合?换句话说,为可以覆盖的方法创建virtual
方法并使用接口来实现必须覆盖的方法?
答案 3 :(得分:0)
我最近遇到了类似的问题(dnt知道它会对你的情况有所帮助)。我必须在编译时而不是运行时生成错误,具有通用功能。我使用的方法是Generics和Interfaces的组合。例子是......
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)。