Корректный способ загрузить блок, найдите класс и вызов выполненными () метод

Действительно хороший способ обнаружения мобильных или планшетных устройств - посмотреть, может ли браузер создать сенсорное событие.

Простой код JavaScript:

function isMobile() {
   try{ document.createEvent("TouchEvent"); return true; }
   catch(e){ return false; }
}

if (isMobile()) {
   # do whatever you wanna do!
}

Это сработало для меня очень хорошо, но могут быть проблемы с ноутбуками, оснащенными сенсорным дисплеем.

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

79
задан Uwe Keim 6 December 2017 в 14:33
поделиться

4 ответа

You will need to use reflection to get the type "TestRunner". Use the Assembly.GetType method.

class Program
{
    static void Main(string[] args)
    {
        Assembly assembly = Assembly.LoadFile(@"C:\dyn.dll");
        Type type = assembly.GetType("TestRunner");
        var obj = (TestRunner)Activator.CreateInstance(type);
        obj.Run();
    }
}
5
ответ дан 24 November 2019 в 10:15
поделиться

If you do not have access to the TestRunner type information in the calling assembly (it sounds like you may not), you can call the method like this:

Assembly assembly = Assembly.LoadFile(@"C:\dyn.dll");
Type     type     = assembly.GetType("TestRunner");
var      obj      = Activator.CreateInstance(type);

// Alternately you could get the MethodInfo for the TestRunner.Run method
type.InvokeMember("Run", 
                  BindingFlags.Default | BindingFlags.InvokeMethod, 
                  null,
                  obj,
                  null);

If you have access to the IRunnable interface type, you can cast your instance to that (rather than the TestRunner type, which is implemented in the dynamically created or loaded assembly, right?):

  Assembly assembly  = Assembly.LoadFile(@"C:\dyn.dll");
  Type     type      = assembly.GetType("TestRunner");
  IRunnable runnable = Activator.CreateInstance(type) as IRunnable;
  if (runnable == null) throw new Exception("broke");
  runnable.Run();
46
ответ дан 24 November 2019 в 10:15
поделиться

When you build your assembly, you can call AssemblyBuilder.SetEntryPoint, and then get it back from the Assembly.EntryPoint property to invoke it.

Keep in mind you'll want to use this signature, and note that it doesn't have to be named Main:

static void Run(string[] args)
2
ответ дан 24 November 2019 в 10:15
поделиться

Я делаю именно то, что вы ищете, в моем движке правил, который использует CS-Script для динамической компиляции, загрузки и выполнения C #. Он должен легко переводиться в то, что вы ищете, и я приведу пример. Во-первых, код (урезанный):

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using CSScriptLibrary;

namespace RulesEngine
{
    /// <summary>
    /// Make sure <typeparamref name="T"/> is an interface, not just any type of class.
    /// 
    /// Should be enforced by the compiler, but just in case it's not, here's your warning.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class RulesEngine<T> where T : class
    {
        public RulesEngine(string rulesScriptFileName, string classToInstantiate)
            : this()
        {
            if (rulesScriptFileName == null) throw new ArgumentNullException("rulesScriptFileName");
            if (classToInstantiate == null) throw new ArgumentNullException("classToInstantiate");

            if (!File.Exists(rulesScriptFileName))
            {
                throw new FileNotFoundException("Unable to find rules script", rulesScriptFileName);
            }

            RulesScriptFileName = rulesScriptFileName;
            ClassToInstantiate = classToInstantiate;

            LoadRules();
        }

        public T @Interface;

        public string RulesScriptFileName { get; private set; }
        public string ClassToInstantiate { get; private set; }
        public DateTime RulesLastModified { get; private set; }

        private RulesEngine()
        {
            @Interface = null;
        }

        private void LoadRules()
        {
            if (!File.Exists(RulesScriptFileName))
            {
                throw new FileNotFoundException("Unable to find rules script", RulesScriptFileName);
            }

            FileInfo file = new FileInfo(RulesScriptFileName);

            DateTime lastModified = file.LastWriteTime;

            if (lastModified == RulesLastModified)
            {
                // No need to load the same rules twice.
                return;
            }

            string rulesScript = File.ReadAllText(RulesScriptFileName);

            Assembly compiledAssembly = CSScript.LoadCode(rulesScript, null, true);

            @Interface = compiledAssembly.CreateInstance(ClassToInstantiate).AlignToInterface<T>();

            RulesLastModified = lastModified;
        }
    }
}

Он возьмет интерфейс типа T, скомпилирует файл .cs в сборку, создаст экземпляр класса заданного типа и выровняет этот экземпляр класса с интерфейсом T. По сути, вам просто нужно убедиться, что созданный класс реализует этот интерфейс. Я использую свойства для настройки и доступа ко всему, например:

private RulesEngine<IRulesEngine> rulesEngine;

public RulesEngine<IRulesEngine> RulesEngine
{
    get
    {
        if (null == rulesEngine)
        {
            string rulesPath = Path.Combine(Application.StartupPath, "Rules.cs");

            rulesEngine = new RulesEngine<IRulesEngine>(rulesPath, typeof(Rules).FullName);
        }

        return rulesEngine;
    }
}

public IRulesEngine RulesEngineInterface
{
    get { return RulesEngine.Interface; }
}

В вашем примере вы хотите вызвать Run (), поэтому я бы создал интерфейс, определяющий метод Run (), например:

public interface ITestRunner
{
    void Run();
}

Затем сделайте класс, который его реализует, например:

public class TestRunner : ITestRunner
{
    public void Run()
    {
        // implementation goes here
    }
}

Измените имя RulesEngine на что-нибудь вроде TestHarness и установите свои свойства:

private TestHarness<ITestRunner> testHarness;

public TestHarness<ITestRunner> TestHarness
{
    get
    {
        if (null == testHarness)
        {
            string sourcePath = Path.Combine(Application.StartupPath, "TestRunner.cs");

            testHarness = new TestHarness<ITestRunner>(sourcePath , typeof(TestRunner).FullName);
        }

        return testHarness;
    }
}

public ITestRunner TestHarnessInterface
{
    get { return TestHarness.Interface; }
}

Затем, где бы вы ни захотели его вызвать, вы можете просто запустить:

ITestRunner testRunner = TestHarnessInterface;

if (null != testRunner)
{
    testRunner.Run();
}

Это, вероятно, отлично подойдет для системы плагинов, но мой Код как есть ограничен загрузкой и запуском одного файла, поскольку все наши правила находятся в одном исходном файле C #. Я бы подумал, что было бы довольно легко изменить его, просто передав тип / исходный файл для каждого из них, который вы хотите запустить. Вам просто нужно переместить код из получателя в метод, который принимает эти два параметра.

Также используйте IRunnable вместо ITestRunner.

поскольку все наши правила находятся в одном исходном файле C #. Я бы подумал, что было бы довольно легко изменить его, просто передав тип / исходный файл для каждого из них, который вы хотите запустить. Вам просто нужно переместить код из получателя в метод, который принимает эти два параметра.

Также используйте IRunnable вместо ITestRunner.

поскольку все наши правила находятся в одном исходном файле C #. Я бы подумал, что было бы довольно легко изменить его, просто передав тип / исходный файл для каждого из них, который вы хотите запустить. Вам просто нужно переместить код из получателя в метод, который принимает эти два параметра.

Также используйте IRunnable вместо ITestRunner.

11
ответ дан 24 November 2019 в 10:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: