简体   繁体   English

用泛型进行继承

[英]Composition over inheritance with generics

Lets say I have two interfaces ISomeInterface and ISomeInterface<T> and ISomeInterface<T> inherites from ISomeInterface . 可以说我有两个接口ISomeInterfaceISomeInterface<T>ISomeInterface<T>继承自ISomeInterface

Now if we have to classes Someclass and SomeClass<T> how can we implement composition over inheritance? 现在,如果我们必须对SomeclassSomeClass<T>进行分类,我们如何实现继承的组合? Which class should have instance of another class? 哪个类应该具有另一个类的实例?

public interface ISomeInterface
{
     string Name {get;}
}

public interface ISomeInterface<T> : ISomeInterface
{
     string ResultMessage{get;}
}

public class SomeClass : ISomeInterface
{
     public string Name
     {
          get{return this.GetType().Name;
     }
}
public class SomeClass<T> ISomeInterface<T>
{
     ISomeInterface _someclassinstance = new SomeClass;

     public string Name
     {
          get{return _someclassInstance.Name;
     }

     public string Caption
     {
          get{return "Demo Caption";}
     }
}

This will not work as when I call Name property on instance of generic version of class it will return SomeInterface not derived class name. 这将不起作用,因为当我在类的泛型版本的实例上调用Name属性时,它将返回SomeInterface而不是派生的类名。

I would like to know if it is good implementation or Non-Generic version should have instance of Generic opposite of what I have done so far? 我想知道它是很好的实现还是非通用版本应该具有与我到目前为止所做的相反的通用实例?

   public abstract class ViewModelBase : NotificationObject, IViewModel
{
    private IViewModel _parent;
    private string _title;
    private bool _isBusy;
    private bool _isDitry;

    #region Viewmodel Initialization steps

    /// <summary>
    /// Initializes a new instance of the <see cref="ViewModelBase"/> class.
    /// </summary>
    internal protected ViewModelBase()
    {
        ResolveDependencies();

        Load();
    }

    /// <summary>
    /// Exposes a way for external users to pass a boxed payload
    /// to this view model for initialization purposes.
    /// Inheritors can override this method to perform specific
    /// initialization and validation operations depending on the
    /// payload sent from the outside.
    /// </summary>
    /// <param name="payload">
    /// Represents the boxed payload the view model uses
    /// for initialization purposes.
    /// </param>
    public virtual void Initialize(object payload)
    {
    }

    /// <summary>
    /// Loads the view model.
    /// </summary>
    private void Load()
    {
        BeforeLoad();
        OnLoad();
    }

    /// <summary>
    /// Gives inheritors the opportunity to perform an operation before the view model loads.
    /// </summary>
    protected virtual void BeforeLoad()
    {
    }

    /// <summary>
    /// Gives inheritors the opportunity to perform specific operations when the view model loads for the first time.
    /// </summary>
    protected virtual void OnLoad()
    {
        SetupEvents();
    }

    /// <summary>
    /// Registers listeners for all events in which the ViewModel is interested in.
    /// </summary>
    protected virtual void SetupEvents()
    {

    }

    /// <summary>
    /// Resolves the dependencies for this view model.
    /// </summary>
    private void ResolveDependencies()
    {
        BeforeResolveDependencies();
        EventAggregator = ServiceLocator.Current.GetInstance<IEventAggregator>();
        OnResolveDependencies();
    }

    /// <summary>
    /// Gives inheritors the opportunity to perform an operation before the dependencies are resolved.
    /// </summary>
    protected virtual void BeforeResolveDependencies()
    {
    }

    /// <summary>
    /// Gives inheritors the opportunity to resolve dependencies of their own.
    /// </summary>
    protected virtual void OnResolveDependencies()
    {
    }

    /// <summary>
    /// Event handler that notifies the new children of the parent
    /// view model they live in and removes the parent when they are
    /// removed from the collection.
    /// </summary>
    /// <param name="sender">Child view model rising the event.</param>
    /// <param name="e">Changes made to the collection.</param>
    protected void ChildCollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.NewItems != null)
        {
            foreach (var item in e.NewItems)
            {
                ((ViewModelBase)item).Parent = this;
            }
        }

        if (e.OldItems == null) return;
        foreach (var item in e.OldItems)
        {
            ((ViewModelBase)item).Parent = null;
        }
    }

    #endregion

    #region Members of IViewModel

    /// <summary>
    /// Gets the title.
    /// </summary>
    public string Title
    {
        get { return _title ?? (_title = GetType().Name); }
        protected set
        {
            _title = value;
            RaisePropertyChanged(() => Title);
        }
    }

    /// <summary>
    /// Gets or sets a value indicating whether the ViewModel is busy.
    /// </summary>
    /// <value><see langword="true"/> if the ViewModel is busy; otherwise, <see langword="false"/>.</value>
    public bool IsBusy
    {
        get { return _isBusy; }
        protected set
        {
            _isBusy = value;
            RaisePropertyChanged(() => IsBusy);
        }
    }

    /// <summary>
    /// Gets or sets a value indicating whether this instance is dirty.
    /// </summary>
    /// <value>
    ///   <c>true</c> if this instance is dirty; otherwise, <c>false</c>.
    /// </value>
    public bool IsDirty
    {
        get { return _isDitry; }
        set
        {
            _isBusy = value;
            RaisePropertyChanged(() => IsDirty);
        }
    }

    /// <summary>
    /// Gets or sets the view for which this instance is set as DataContext.
    /// </summary>
    /// <value>
    /// The view.
    /// </value>
    public IView View { get; set; }

    /// <summary>
    /// Gets the parent ViewModel holding this instance as
    /// childe ViewModel collection item.
    /// </summary>
    public IViewModel Parent
    {
        get { return _parent; }
        set
        {
            _parent = value;
            RaisePropertyChanged(() => Parent);
        }
    }

    #endregion

    /// <summary>
    /// Get CurrentInstance of EventAggregator
    /// </summary>
    private IEventAggregator EventAggregator { get; set; }

    /// <summary>
    /// Gets the dispatcher.  Works at run-time and test-time.
    /// </summary>
    /// <value>The dispatcher.</value>
    protected static Dispatcher Dispatcher
    {
        get { return Application.Current != null ? Application.Current.Dispatcher : Dispatcher.CurrentDispatcher; }
    }


    /// <summary>
    /// Resolves this instance for specific type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    protected static T Resolve<T>()
    {
        try
        {
            return ServiceLocator.Current.GetInstance<T>();
        }
        catch (Exception)
        {
            return default(T);
        }
    }

    /// <summary>
    /// Resolves all instances of specific type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    protected static IEnumerable<T> ResolveAll<T>()
    {
        return ServiceLocator.Current.GetAllInstances<T>();
    }

    /// <summary>
    /// Registers for specific event inside system event providing some filter parameters.
    /// </summary>
    /// <typeparam name="TEvent">The type of the event.</typeparam>
    /// <param name="onEvent">The on event.</param>
    /// <param name="filter">The filter.</param>
    protected void RegisterForEvent<TEvent>(Action<TEvent> onEvent, Predicate<TEvent> filter = null) where TEvent : class
    {
        EventAggregator.Subscribe<TEvent>(onEvent, ThreadOption.PublisherThread, false, filter);
    }

    /// <summary>
    /// Broadcasts the specified event object.
    /// </summary>
    /// <typeparam name="TEvent">The type of the event.</typeparam>
    /// <param name="eventObject">The event object.</param>
    protected void Broadcast<TEvent>(TEvent eventObject) where TEvent : class
    {
        EventAggregator.Publish(eventObject);
    }
}

this is thi complete implementation of IViewModel base class and 这是IViewModel基类的完整实现,并且

    public abstract class ViewModelBase<TValidator> : ViewModelBase,IViewModel<TValidator>, IDataErrorInfo
    where TValidator : IValidator, new()
{
    private bool _islValid;
    readonly IValidator _validator = new TValidator();

    /// <summary>
    /// Get Validation processor for ViewmodelInstance 
    /// </summary>
    /// <value>
    /// The Validator.
    /// </value>
    public TValidator Validator
    {
        get { return (TValidator) _validator; }
    }

    public bool IsValid
    {
        get
        {
            _islValid = SelfValidate().IsValid;
            return _islValid;
        }
        private set
        {
            _islValid = value;                
            RaisePropertyChanged(() => IsValid);
        }
    }
// Implementation for validation
}

Here I posted source for both classes. 在这里,我发布了两个类的源代码。 and there are N number of differenc interfaces and classes which all need to derive either from Generic or Non-Generic version of these classes. 并且有N个不同的接口和类,它们都需要从这些类的通用或非通用版本中派生。 put properties available in IViewmodel should be available on every derived class and protected methods and properties needs to be kept as well IViewmodel中可用的put属性应该在每个派生类上都可用,并且还应保留受保护的方法和属性

Regarding the composition, generic version needs to use the non-generic version. 关于组成,通用版本需要使用非通用版本。

You can't go the opposite way, because the generic version of the class is the strongly typed one, which means it contains more compile time detail than the non-generic version. 您不能反其道而行之,因为该类的泛型版本是强类型化的类,这意味着它比非泛型版本包含更多的编译时间细节。 There would be no way to pass the required types to the inner generic class from the non-generic class. 无法将所需类型从非泛型类传递到内部泛型类。

As of the Name property, it actually does exactly what it is supposed to do, return the Name of the SomeClass type. Name属性开始,它实际上确实执行了应做的事情,返回SomeClass类型的Name

Edit: 编辑:

If you need a way to always return the name of the current type, then there is many ways how you could go about this. 如果您需要一种始终返回当前类型名称的方法,那么有很多方法可以解决此问题。 You could make a normal or generic helper method for this, or for example you could make an extension method for the ISomeInterface type and use it instead of the Name property: 您可以为此创建一个普通或通用的辅助方法,例如,可以为ISomeInterface类型创建一个扩展方法,并使用它代替Name属性:

public static class SomeInterfaceExtensions
{
    // Extension
    public static string GetName(this ISomeInterface some)
    {
        return some.GetType().Name;
    }
}

// Usage
var some = new SomeClass<int>();
var name = some.GetName();

But all you will achieve is just to make a shortcut to the GetType().Name , as all the types have these already. 但是,您所要做的只是为GetType().Name创建一个快捷方式,因为所有类型都已经具备了这些类型。

I don't see much value in this solution, at least in the given example with the type name. 我在此解决方案中看不到太多价值,至少在给定示例中类型名称为。

Also this is in no way related to generics vs non-generics or all the other stuff mentioned. 同样,这与泛型与非泛型或提及的所有其他内容完全无关。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM