Препроцессор: получить операционную систему .Net Core

Я пишу класс, который хочу использовать как в Windows, так и в Linux.

Один из методов этого класса — доступ к Реестр Windows

Чего я надеюсь добиться, так это каким-то образом запретить использование этого конкретного метода при использовании машины с Linux.
< br>Прежде всего я провел небольшое исследование, чтобы узнать, есть ли что-то для .Net Core это позволило бы мне проверить, какая операционная система используется, я нашел это и, конечно же, это работает.

Когда я реализовал это в своем коде при доступе к методу, я надеялся отключить метод, который обращается к реестру Windows, однако ближе всего к этому я смог добраться с помощью оператора switch. , что-то вроде этого

switch (OS)
{
    case OSX:
    return;

    case LINUX:
    return
}

К return, если операционная система не поддерживалась, это работало, однако я тогда подумал, что будет гораздо лучше отключить доступ к ней все вместе, чем выдавать ошибку для операционной системы, которая не поддерживается для этого конкретного метода

Затем я перешел к директивам препроцессора думая, что если я смогу обнаруживать и отключать части кода в зависимости от фреймворков и т. д., возможно, я мог бы использовать что-то подобное, чтобы отключить части кода в зависимости от операционной системы, чтобы они никогда не могли быть вызваны даже при попытке доступа метод

Я продолжил оттуда, чтобы посмотреть, смогу ли я отключить части кода с помощью preprocessor directives.
Я нашел это.

Я понимаю, что это для C++ однако он кажется наиболее близким к тому, чего я пытаюсь достичь в рамках .Net Core

В идеальном мире это выглядело бы примерно так

    /// <summary>
    /// Get the file mime type
    /// </summary>
    /// <param name="filePathLocation">file path location</param>
    /// <returns></returns>
    `#if WINDOWS`
    public static string GetMimeType(this string filePathLocation)
    {
        if (filePathLocation.IsValidFilePath())
        {
            string mimeType = "application/unknown";
            string ext = Path.GetExtension(filePathLocation).ToLower();
            Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);

            if (regKey != null && regKey.GetValue("Content Type") != null)
            {
                mimeType = regKey.GetValue("Content Type").ToString();
            }
            return mimeType;
        }
        return null;
    }
`#endif`

Я видел #Define, поэтому я попробовал что-то вроде этого #define IS_WINDOWS и добавил его в свой класс вместе с #if IS_WINDOWS, однако я не мог понять, как изменить это значение, если я надеюсь просто повторно использовать статический класс снова и снова.


person AndrewE    schedule 12.09.2018    source источник
comment
Не могли бы вы объяснить, что вам не нравится в методе работы, который вы уже нашли? Честно говоря, я не понимаю, почему вы не хотите его использовать. Также я не понимаю, почему вы хотите использовать директивы препроцессора. Эти директивы определяются и передаются компилятору до начала компиляции. Так что, возможно, я ошибаюсь, но похоже, что, например, вы не сможете скомпилировать свою программу в Windows и запустить ее в Linux. В этом случае ваше #if WINDOWS было бы правдой, хотя на самом деле это не так...   -  person Jérôme MEVEL    schedule 21.09.2018


Ответы (1)


Хотя вы могли бы пойти по маршруту, включающему #define, это время компиляции, и вы потеряете много многоплатформенного совершенства .Net. Вам также придется жонглировать несколькими конфигурациями, несколькими сборками и т. д.

По возможности скройте поведение, зависящее от платформы, за независимой от платформы абстракцией и выполняйте проверку во время выполнения с помощью System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform:

interface IPlatform
{
    void DoSomething();
}

class WindowsImpl : IPlatform
{
    public void DoSomething()
    {
        // Do something on Windows
    }
}

class LinuxImpl : IPlatform
{
    public void DoSomething()
    {
        // Do something on Linux
    }
}

// Somewhere else
var platform = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? new WindowsImpl() : new LinuxImpl();
platform.DoSomething();

Это хорошо работает во многих случаях, включая PInvoke. Вы сможете использовать одни и те же двоичные файлы на любой платформе, и позже будет проще добавить OSX.

Если вам нужно изолировать зависящий от платформы код во время компиляции (возможно, пакет предназначен только для Windows), MEF2/System.Composition может помочь вам создать структуру плагинов, в которой для каждой платформы будет своя сборка:

// In Windows.dll class library project
using System.Composition;

[Export(typeof(IPlatform))]
public class WindowsImpl : IPlatform
{
    public void DoSomething()
    {
        //...
    }
}

И затем в вашей основной программе:

using System.Composition.Hosting;

var configuration = new ContainerConfiguration();
var asm = Assembly.LoadFrom(pathToWindowsDll);
configuration.WithAssembly(asm);
var host = configuration.CreateContainer();
var platform = host.GetExports<IPlatform>().FirstOrDefault();
person Jake    schedule 12.09.2018
comment
Эй, Джейк, спасибо за ваш ответ, у меня небольшие проблемы с пониманием вашего ответа, у меня есть статический класс с кучей вспомогательных расширений для ядра .net. Одно из расширений получает MIME-тип файла, обращаясь к реестру, что совершенно невозможно в Linux и Mac, я не вижу, как интерфейсы могут помочь, поскольку у меня есть только один класс с этим методом, есть ли что-то, что мне не хватает? или ваша идея заключалась в том, как использовать интерфейсы с двумя классами и повторять код между ними? еще раз спасибо - person AndrewE; 14.09.2018
comment
Я говорю, что вы, возможно, захотите рассмотреть возможность использования проверки во время выполнения, чтобы выбрать правильное поведение, а не делать это во время компиляции. Возьмите свою функцию GetMimeType(). Конечно, реализация Windows может использовать реестр, но, скорее всего, также будет реализация Linux, которая использует что-то еще. У меня нет под рукой виртуальной машины Linux, но этот код компилируется в OSX. Во время выполнения происходит сбой с System.Security.SecurityException на OpenSubKey(), но дело в том, что вам не нужно #ifdef везде (включая все места, где вызывается функция). - person Jake; 14.09.2018
comment
Если вы действительно хотите отключить его, но не иметь дело со всеми местами, которые его вызывают, вы также можете использовать ConditionalAttribute - person Jake; 14.09.2018
comment
Спасибо, Джейк, да, я уверен, что действительно есть что-то для Linux, возможно, я смогу реализовать что-то еще, мне придется провести некоторое исследование, да, я видел ConditionalAttribute, однако, прежде чем компилировать, я думаю, вам нужно сделать что-то вроде этого set OS_WINDOWS = 1 для того, чтобы использовать только сторону Windows, я пытался сделать так, чтобы одна dll управляла ими всеми, мне может понадобиться создать разные пакеты, я не знаю на данном этапе, мы увидим, я думаю. - person AndrewE; 14.09.2018