Страницы

Поиск по вопросам

воскресенье, 1 декабря 2019 г.

Как продуктивно создавать оболочки совместимости (shims), для .Net Core, Framework, Standard

#c_sharp #net #net_core #netcore20 #net_standard


Как продуктивно создавать оболочки совместимости (shims), для .Net Core, Framework,
Standard?

Версии: Framework 4.6.1; Core 2.0; Standard 2.0.

Например, интересуют следующие 3 штуки, для использования между .net core, .net standard
и .net framework:

System.Windows.Threading.Dispatcher, System.ComponentModel.ItemPropertyInfo.Descriptor,
даже 
System.Windows.Controls.MenuItem.

На самом деле, похоже, что таких оболочек нужно намного больше. Безусловно, их можно
создавать вручную. Но может быть есть более продуктивный способ, чтобы избежать механической
работы?



Пояснение задачи на грубом примере, если делать вручную: 

Например, для Core 2.0 не реализован Dispatcher.

Делается абстрактная оболочка / интерфейс / фасад, :

public enum DispatcherShimPriority
{
    Background
    //...
}

public interface DispaicherShim
{
    void Invoke(Action action, DispatcherShimPriority prio);
    void BeginInvoke(Action action, DispatcherShimPriority, prio);
}


Далее 2 реализации:

public class DispatcherCore: DispaicherShim;
//здесь по началу можно просто вызывать Action


и

public class DispatcherFramework: DispaicherShim;
//здесь используется реальный Dispatcher внутри


Далее делается какой-то мультинацеленный класс-активатор, например, Shims, в котором:

public static DispaicherShim CreateDispatcher()
{
#if NETCOREAPP2_0
    return new DispatcherCore();
#else
    return new DispatcherFramework();
#endif       
}


Таким образом, получается оболочка, которую можно использовать как в Framework-овских,
так и в Core-овских приложениях.

Создание таких оболочек требует много механической работы. Интуитивно мне кажется,
что эту работу проделывать не обязательно, что есть уже готовые решения...



Про Microsoft.Windows.Compatibility pack в курсе. Имею ввиду создание оболочек для
элементов, не покрывающихся данным пакетом.

Про Microsoft.Windows.Compatibility.Shims слышал, но подозреваю, что там нет оболочек
для элементов, не покрывающихся самим пакетом.



Общая задача состоит в том, чтобы основную часть WPF приложения перевести на кор
под потенциальный web-client (оставляя работающий WPF), при том, что многие элементы
.net framework основной части не переводятся на кор.
    


Ответы

Ответ 1



К этому моменту нашел, по крайней мере, удовлетворяющие способы создания оболочек совместимости. Может есть более продуктивные методы. Спасибо Firda из Чехии. Вот его ответ 1) В принципе достаточно простой generic оболочки public abstract class Shim { internal TImpl It { get; } protected Shim(TImpl it) { It = it; } } EXAMPLE: public class DispatcherPriorityShim : Shim< #if NETFULL DispatcherPriority #elif NETCORE string #endif > { public DispatcherPriorityShim(string it) #if NETFULL : base((DispatcherPriority)Enum.Parse(typeof(DispatcherPriority), it)) #elif NETCORE : base(it) #endif { } } 1.a) Visual Studio snippets drv #if NETFULL #elif NETCORE #endif shimenum namespace PortabilityLibrary.Shims { public class $enumname$Shim : Shim< #if NETFULL $enumname$ #elif NETCORE string #endif > { public $enumname$Shim(string it) #if NETFULL : base(($enumname$)Enum.Parse(typeof($enumname$), it)) #elif NETCORE : base(it) #endif { } } } shimsnip namespace PortabilityLibrary.Shims { public class $classname$Shim : Shim< #if NETFULL $classname$ #elif NETCORE $classname$ //NullObject #endif > { public $classname$Shim() #if NETFULL : base(new $classname$()) #elif NETCORE : base(new $classname$()) //: base(new NullObject()) #endif {} } } shimmeth public void $methodname$() { #if NETFULL It.$methodname$(); #elif NETCORE It.$methodname$(); //throw new ShimException(); #endif } shimprop - аналогично, пока не сделал 1.b) Пояснение на счет NETCORE и NETFULL Sdk-style .csproj file to make clear about NETFULL and NETCORE: netstandard2.0;netcoreapp2.0;net461 NETCORE; NETFULL; 2) Более продвинутый вариант позволяющий наследственность public interface IShimOne { void MethodOne(); } public interface IShimTwo: IShimOne { void MethodTwo(); } class One: RealOne, IShimOne {} class Two: RealTwo, IShimTwo {} public static class ShimFactory { public static IShimOne CreateOne() { return new One(); } public static IShimTwo CreateTwo() { return new Two(); } } 2.a) Объекты public class WrapperOne { protected IShimOne It { get; } protected WrapperOne(IShimOne it) { It = it; } public WrapperOne() { It = ShimFactory.CreateOne(); } public void MethodOne() { It.MethodOne(); } } public class WrapperTwo: WrapperOne { protected new IShimTwo It => (IShimTwo)base.It; protected WrapperTwo(IShimTwo it): base(it) {} public WrapperTwo(): base(ShimFactory.CreateTwo()) {} public void MethodTwo() { It.MethodTwo(); } 3) Готовые "двойники" для GUI контролов (Eto.Forms) (вообще, Eto.Forms имеет более широкое применение - они сами по себе уже оболочки) //Не до конца сделано, просто чтобы показать идею: #if NETFULL using System.Windows.Controls; #elif NETCORE using Eto.Forms; #endif namespace PortabilityLibrary.Shims { public class MenuItemShim : Shim< #if NETFULL MenuItem #elif NETCORE MenuItem #endif > { public MenuItemShim(EventHandler dlg) #if NETFULL : base(new MenuItem(/*not implemented*/)) #elif NETCORE : base(new ButtonMenuItem(dlg)) #endif { } } }

Комментариев нет:

Отправить комментарий