كيف يمكن محاكاة متعددة-الميراث و استخدام انعكاس لتحسين هذا الرمز ؟

StackOverflow https://stackoverflow.com/questions/1027445

سؤال

لدي تطبيق WPF حيث PageItems هي نموذج الكائنات.

بلدي الرئيسية ViewModel لديه ObservableCollection من PageItemViewModels, كل واحد بناء نفسها من مطابقة PageItem نموذج كائن.

كل PageItemViewModel يرث من فئة مجردة BaseViewModel من أجل الحصول على INotifyPropertyChanged وظائف.

كل PageItemViewModel كما تنفذ IPageItemViewModel من أجل التأكد من أنها في حاجة خصائص.

سوف يكون في نهاية المطاف حوالي 50 صفحة لذلك أريد أن القضاء على أي قانون لا لزوم لها:

  • حل (انظر أدناه):هل هناك طريقة يمكنني الحصول على PageItemViewModel دروس ترث IdCode و العنوان لذلك أنا لم يكن لديك لتنفيذها في كل فصل ؟ لا أستطيع وضعها في BaseViewModel منذ أخرى ViewModels ترث منه الذي لا تحتاج إلى هذه الخصائص لا أستطيع وضعها في IPageItemViewModel لأنها ليست سوى واجهة.أنا أفهم أنا بحاجة وراثة متعددة هذا الذي C# لا يدعم
  • حل (انظر أدناه):هل هناك طريقة يمكنني التخلص من التبديل البيان, على سبيل المثالبطريقة أو بأخرى استخدام انعكاس بدلا من ذلك ؟

أدناه بذاتها تطبيق وحدة التحكم مما يدل على رمز لدي في بلدي WPF التطبيق:

using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<PageItem> pageItems = PageItems.GetAll();
            List<ViewModelBase> pageItemViewModels = new List<ViewModelBase>();

            foreach (PageItem pageItem in pageItems)
            {
                switch (pageItem.IdCode)
                {
                    case "manageCustomers":
                        pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem));
                        break;
                    case "manageEmployees":
                        pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem));
                        break;
                    default:
                        break;
                }
            }
        }
    }

    public class PageItemManageCustomersViewModel : ViewModelBase, IPageItemViewModel
    {
        public string IdCode { get; set; }
        public string Title { get; set; }

        public PageItemManageCustomersViewModel(PageItem pageItem)
        {

        }
    }

    public class PageItemManageEmployeesViewModel : ViewModelBase, IPageItemViewModel
    {
        public string IdCode { get; set; }
        public string Title { get; set; }

        public PageItemManageEmployeesViewModel(PageItem pageItem)
        {

        }
    }

    public interface IPageItemViewModel
    {
        //these are the properties which every PageItemViewModel needs
        string IdCode { get; set; }
        string Title { get; set; }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItems
    {
        public static List<PageItem> GetAll()
        {
            List<PageItem> pageItems = new List<PageItem>();
            pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"});
            pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"});
            return pageItems;
        }
    }

}

بتعميل الترميز:واجهة تغيير فئة مجردة

using System;
using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<PageItem> pageItems = PageItems.GetAll();
            List<ViewModelPageItemBase> pageItemViewModels = new List<ViewModelPageItemBase>();

            foreach (PageItem pageItem in pageItems)
            {
                switch (pageItem.IdCode)
                {
                    case "manageCustomers":
                        pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem));
                        break;
                    case "manageEmployees":
                        pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem));
                        break;
                    default:
                        break;
                }
            }

            foreach (ViewModelPageItemBase pageItemViewModel in pageItemViewModels)
            {
                System.Console.WriteLine("{0}:{1}", pageItemViewModel.IdCode, pageItemViewModel.Title);
            }
            Console.ReadLine();
        }
    }

    public class PageItemManageCustomersViewModel : ViewModelPageItemBase
    {
        public PageItemManageCustomersViewModel(PageItem pageItem)
        {
            IdCode = pageItem.IdCode;
            Title = pageItem.Title;
        }
    }

    public class PageItemManageEmployeesViewModel : ViewModelPageItemBase
    {
        public PageItemManageEmployeesViewModel(PageItem pageItem)
        {
            IdCode = pageItem.IdCode;
            Title = pageItem.Title;
        }
    }

    public abstract class ViewModelPageItemBase : ViewModelBase
    {
        //these are the properties which every PageItemViewModel needs
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItems
    {
        public static List<PageItem> GetAll()
        {
            List<PageItem> pageItems = new List<PageItem>();
            pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"});
            pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"});
            return pageItems;
        }
    }

}

الجواب على القضاء على بيان التبديل:

شكرا بالكوع:

string assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
string viewModelName = assemblyName + ".ViewModels.PageItem" + StringHelpers.ForcePascalNotation(pageItem.IdCode) + "ViewModel";
var type = Type.GetType(viewModelName);
var viewModel = Activator.CreateInstance(type, pageItem) as ViewModelBase;
AllPageViewModels.Add(viewModel);
هل كانت مفيدة؟

المحلول

أحد الحلول التي ليست جميلة جدا ، ولكن يعمل استخدام اتفاقية للتخلص من بيان التبديل.هذا يفترض يمكنك تغيير IdCodes أو على الأقل تعديل القضية إلى المباراة ViewModel.

    var type = Type.GetType("PageItem" + pageItem.IdCode + "ViewModel");
    var viewModel = Activator.CreateInstance(type) as ViewModelBase;
    pageItemViewModels.Add(viewModel);

لاحظ أنه يجب إضافة تدقيق الأخطاء هنا وهناك بضع نقاط الفشل هنا.بيد أنه أفضل من الحاجة إلى الحفاظ على متزايدا بيان التبديل.

نصائح أخرى

يمكنك إنشاء فئة يرث من BaseViewModel أن تنفيذ هاتين الخاصيتين الخاص بك PageItemViewModel الفئات التي تحتاج هذا ثم يمكن أن ترث من ذلك.

كما اقترح بادي, أنا خلقت فقط إضافية فئة مجردة, PageViewModelBase مع تلك السيارات الدعائم محددة:

using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<PageItem> pageItems = PageItems.GetAll();
            List<ViewModelBase> pageItemViewModels = new List<ViewModelBase>();

            foreach (PageItem pageItem in pageItems)
            {
                switch (pageItem.IdCode)
                {
                    case "manageCustomers":
                        pageItemViewModels.Add(new PageItemManageCustomersViewModel(pageItem));
                        break;
                    case "manageEmployees":
                        pageItemViewModels.Add(new PageItemManageEmployeesViewModel(pageItem));
                        break;
                    default:
                        break;
                }
            }
        }
    }

    public class PageItemManageCustomersViewModel : PageViewModelBase
    {
        public PageItemManageCustomersViewModel(PageItem pageItem)
        {

        }
    }

    public class PageItemManageEmployeesViewModel : PageViewModelBase
    {
        public PageItemManageEmployeesViewModel(PageItem pageItem)
        {


        }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public abstract class PageViewModelBase : ViewModelBase
    {
        //these are the properties which every PageItemViewModel needs
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    public class PageItems
    {
        public static List<PageItem> GetAll()
        {
            List<PageItem> pageItems = new List<PageItem>();
            pageItems.Add(new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers"});
            pageItems.Add(new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees"});
            return pageItems;
        }
    }

}

لماذا لا يمكنك وضع GetViewModel() أسلوب الظاهري في قاعدة الخاص بك PageItem الدرجة التي ترجع المناسبة عرض النموذج ؟

   foreach (PageItem pageItem in pageItems)
   {
       pageItemViewModels.Add(pageItem.GetViewModel());
   }

الشيء الذي على الفور يشبه رمز رائحة استخدام "id" خصائص - هذه عادة يمكن استبدال مع تعدد الأشكال.لذا سيكون محل switch البيان مع رمز أعلاه.

تحرير:

إذا كان الخاص بك PageItem فئة لا يعرف شيئا عن عرض النموذج ثم فإنه لا يمكن تنفيذها بهذه الطريقة.في الأساس, كنت في حاجة الى المصنع الذي لديك بالفعل (في الطريق).

وعادة ما لديك قائمة من العلاقات (PageItem إلى ViewModel) ، في هذه الحالة يكون Dictionary<String, Type>.ثم يمكنك ملء هذه القائمة أثناء التهيئة و المناسبة عرض نموذج مثيل في وقت لاحق.

استخدام انعكاس لبناء هذه القائمة ، يمكنك على الأقل بحاجة إلى معرفة برمجيا أي صفحة البند معتمد من قبل نموذج عرض.لهذا الغرض يمكنك استخدام سمة مخصصة لتزيين كنت الفئة ، على سبيل المثال.:

public class SupportsPageItemAttribute : Attribute
{
    private readonly string _id;
    public string ID
    {
        get { return _id;}
    }

    public SupportsPageItemAttribute(string id)
    {
        _id = id;
    }
}

ومن ثم استخدام تلك السمة التي تحدد PageItem النموذج الخاص بك يمكن أن نقبل:

[SupportsPageItemAttribute("manageCustomers")
public class PageItemManageCustomersViewModel
{
   // ...
}

ثم يمكنك استخدام انعكاس على جميع الطبقات تنفيذ IPageItemViewModel والتحقق من صفاتهم للحصول على PageItem string id.

على سبيل المثال (دون تدقيق الأخطاء):

Dictionary<String, Type> modelsById = new Dictionary<String, Type>();
String viewModelInterface = typeof(IPageItemViewModel).FullName;

// get the assembly
Assembly assembly = Assembly.GetAssembly(typeof(IPageItemViewModel));

// iterate through all types
foreach (Type viewModel in assembly.GetTypes())
{
    // get classes which implement IPageItemViewModel
    if (viewModel.GetInterface(viewModelInterface) != null)
    {
        // get the attribute we're interested in
        foreach (Attribute att in Attribute.GetCustomAttributes(viewModel))
        {
            if (att is SupportsPageItemAttribute)
            {
                // get the page item id
                String id = (att as SupportsPageItemAttribute).ID;

                // add to dictionary
                modelsById.Add(id, viewModel);
            }
        }
    }
}

ومن ناحية أخرى ، هناك العديد من انعكاس من أطر الرقابة قد تفكر بدلا من القيام سيئة انعكاس العمل بنفسك.

أحد الحلول الممكنة هو عكس العلاقة بين PageItem و PageItemViewModel في التعليمات البرمجية الخاصة بك.الآن أنت توليد PageItemViewModel على أساس PageItem, ولكن ماذا لو قمت بإنشائه PageItemViewModels أولا ثم في كل PageItemViewModel's منشئ, قمت بإنشائه المناسبة PageItem?هذا يلغي الحاجة switch و يجعل الأمور أكثر نظافة ، لأنه الآن وجهة نظركم-نموذج هو المسؤول عن هذا النموذج ، بدلا من نموذج كونها المسؤولة عن عرض نموذج.

على سبيل المثال على أساس القانون الحالي الخاص بك:

using System;
using System.Collections.Generic;

namespace TestInstantiate838
{
    public class Program
    {
        static void Main(string[] args)
        {
            List<ViewModelPageItemBase> pageItemViewModels = PageItemViewModels.GetAll();

            // No switch needed anymore. Each PageItem's view-model contains its PageItem
            // which is exposed as property of the view-model.
            foreach (ViewModelPageItemBase pageItemViewModel in pageItemViewModels)
            {
                System.Console.WriteLine("{0}:{1}", pageItemViewModel.PageItem.IdCode, pageItemViewModel.PageItem.Title);
            }
            Console.ReadLine();
        }
    }

    public class PageItemManageCustomersViewModel : ViewModelPageItemBase
    {
        public PageItemManageCustomersViewModel()
        {
            PageItem = new PageItem { IdCode = "manageCustomers", Title = "ManageCustomers" };
        }
    }

    public class PageItemManageEmployeesViewModel : ViewModelPageItemBase
    {
        public PageItemManageEmployeesViewModel()
        {
            PageItem = new PageItem { IdCode = "manageEmployees", Title = "ManageEmployees" };
        }
    }

    public abstract class ViewModelPageItemBase : ViewModelBase
    {
        //The PageItem associated with this view-model
        public PageItem PageItem { get; protected set; }
    }

    public abstract class ViewModelBase
    {
        protected void OnPropertyChanged(string propertyName)
        {
            //this is the INotifyPropertyChanged method which all ViewModels need
        }
    }

    public class PageItem
    {
        public string IdCode { get; set; }
        public string Title { get; set; }
    }

    // Replaces PageItems class
    public class PageItemViewModels
    {
        // Return a list of PageItemViewModel's instead of PageItem's.
        // Each PageItemViewModel knows how to build it's corresponding PageItem object.
        public static List<PageItemViewModelBase> GetAll()
        {
            List<PageItemViewModelBase> pageItemViewModels = new List<PageItemViewModelBase>();
            pageItemViewModels.Add(new PageItemManageCustomersViewModel());
            pageItemViewModels.Add(new PageItemManageEmployeesViewModel());
            return pageItemViewModels;
        }
    }
} 
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top