Приведение к интерфейсу вызывает исключение InvalidCastException - PullRequest
1 голос
/ 07 мая 2020

Я использую C ++ / CLI как промежуточное ПО между моим игровым сервером C ++ и (будущими) C# плагинами. Предполагается, что он загружает все типы из загруженной сборки, которые реализуют определенный интерфейс с именем IPlakScript, конструирует их и возвращает собственный интерфейс для них на сервер, и он делает именно это, за исключением того, что по какой-то причине объект преобразуется в IPlakScript терпит неудачу с InvalidCastException. Вот мой код:

Код C ++ / CLI:

    public interface class IPlakScript
    {
    public:
        void OnScriptInit(Server^ server);
        void OnScriptShutdown();
    };
    static IScript* LoadGamemodeM(String^ name)
    {
        try {
            auto assembly = Assembly::LoadFrom(name);
            auto types = assembly->GetTypes();
            for each (auto t in types)
            {
                LogWrite("Found some class " + t->Module->FullyQualifiedName);
                LogWrite("IPlakScript mdl " + IPlakScript::typeid->Module->FullyQualifiedName);

                for each (auto i in t->GetInterfaces())
                    LogWrite("IMPLEMENTS: " + i->FullName);

                for each (auto m in t->GetMembers())
                    LogWrite("MEMBER: " + m->Name + " " + m->MemberType.ToString());

                if (t->GetInterface("PlakMP.IPlakScript")) // aka implements IPlakScript
                {
                    LogWrite("Found gamemode class " + t->FullName);
                    Object^ obj = t->GetConstructor(gcnew cli::array<System::Type^, 1> {})->Invoke(gcnew cli::array<System::Object^, 1> {});
                    for each (auto m in t->GetMethods())
                        LogWrite("METHOD: " + m->Name);
                    IPlakScript^ ps = safe_cast<IPlakScript^>(obj); // the exception is thrown here, even though the exact same operation is perfectly fine in C#: why?
                    return new Script(ps);
                    //ps->OnScriptInit(gcnew PlakMP::Server(g_pScriptable));
                }
            }
        }
        catch (Exception^ e)
        {
            String^ description = "PSEsharp exception: " + e->ToString();
            LogWrite(description);
        }
        return nullptr;
    }

C# код:

using System;
using PlakMP;

namespace CSharpGamemodeTest
{
    public class TestGamemode : IPlakScript
    {
        public TestGamemode()
        {
            Console.WriteLine("TestGamemode constructed");

            if (this is IPlakScript) // evaluates to true, thus it's not a C# problem
                Console.WriteLine("I'm PlakScript!");
        }
        public void OnScriptInit(Server server)
        {
            _server = server;

            _server.LogWrite("Hello from C#!");
        }
        public void OnScriptShutdown()
        {
            _server.LogWrite("Goodbye from C#!");
        }
        private Server _server;
    }
}

Вывод журнала:

[2020-05-07 09:34:43] Found some class E:\PlakMP\Sources\Release\plakscript\CSharpGamemodeTest.dll
[2020-05-07 09:34:43] IPlakScript mdl E:\PlakMP\Sources\Release\plakscript\PSEsharp.dll
[2020-05-07 09:34:43] IMPLEMENTS: PlakMP.IPlakScript
[2020-05-07 09:34:43] MEMBER: OnScriptInit Method
[2020-05-07 09:34:43] MEMBER: OnScriptShutdown Method
[2020-05-07 09:34:43] MEMBER: GetType Method
[2020-05-07 09:34:43] MEMBER: ToString Method
[2020-05-07 09:34:43] MEMBER: Equals Method
[2020-05-07 09:34:43] MEMBER: GetHashCode Method
[2020-05-07 09:34:43] MEMBER: .ctor Constructor
[2020-05-07 09:34:43] Found gamemode class CSharpGamemodeTest.TestGamemode
TestGamemode constructed
I'm PlakScript!
[2020-05-07 09:34:43] METHOD: OnScriptInit
[2020-05-07 09:34:43] METHOD: OnScriptShutdown
[2020-05-07 09:34:43] METHOD: GetType
[2020-05-07 09:34:43] METHOD: ToString
[2020-05-07 09:34:43] METHOD: Equals
[2020-05-07 09:34:43] METHOD: GetHashCode
[2020-05-07 09:34:43] PSEsharp exception: System.InvalidCastException: Unable to cast object of type 'CSharpGamemodeTest.TestGamemode' to type 'PlakMP.IPlakScript'.
   at PSManagedWrapper.LoadGamemodeM(String name) in E:\PlakMP\Sources\PSEsharp\PSEsharp.cpp:line 123

По общему признанию, код представляет собой довольно отвратительный беспорядок: но эй, я впервые работаю с C ++ / CLI! Любая помощь будет принята с благодарностью, заранее спасибо.

EDIT: Кажется, что тип IPlakScript определен где-то дважды. Понятия не имею, как это произошло: сборка TestGamemode использует копий типов PSEsharp? Помощь все еще нужна.

...