C # / Unity3D: CreateDelegate с первым аргументом: неверное изменение входных аргументов при вызове - PullRequest
0 голосов
/ 12 мая 2018

Добрый день,

После определения делегата универсального типа я создаю делегат на основе различных методов, вводящих первый аргумент (T1), поэтому при его вызове остается только второй аргумент (T2),Проблема в том, что, хотя он «технически» принимает операцию, входные данные при вызове функции становятся странными для функции «Invoke», но корректны для «DynamicInvoke».Вот два примера (обратите внимание, я использую Unity, поэтому я использую класс Vector2 и метод Debug.Log вместо Console.WriteLine):

public delegate T TDelegate<T, T1>(T1 arg1);
public delegate T TDelegate<T, T1, T2>(T1 arg1, T2 arg2);

public struct AuxSegment
{
    public Vector2 p1;
    public Vector2 p2;
    public AuxSegment (Vector2 n1, Vector2 n2) { p1 = n1; p2 = n2; }
    public string ToString () { return "" + p1.ToString() + ", " + p2.ToString(); }
}

public static bool Method1 (char arg1, string arg2)
{
    Debug.Log("arg1(char) -> " + arg1 + "   arg2(string) -> " + arg2);
    return true;
}

public static bool Method2 (Vector2 arg1, AuxSegment arg2)
{
    Debug.Log("arg1(Vector2) -> " + arg1.ToString() + "    arg2(AuxSegment) -> " + arg2.ToString());
    return true;
}

public void main () {
    char mt1arg1 = 'c';
    string mt1arg2 = "xyz";

    Vector2 mt2arg1 = new Vector2(1,1);
    AuxSegment mt2arg2 = new AuxSegment(new Vector2(5,0), new Vector2(5,10));

    var mt1 = ((TDelegate<bool, char, string>)Method1).Method;
    var mt2 = ((TDelegate<bool, Vector2, AuxSegment>)Method2).Method;

    var del1 = (TDelegate<bool, string>) Delegate.CreateDelegate(typeof(TDelegate<bool, string>), mt1arg1, mt1);
    var del2 = (TDelegate<bool, AuxSegment>) Delegate.CreateDelegate(typeof(TDelegate<bool, AuxSegment>), mt2arg1, mt2);

    Debug.Log("Target/1st arg of Method1 -> " + del1.Target);
    Debug.Log("Target/1st arg of Method2 -> " + del2.Target);

    Debug.Log("\nDynamic Invoke:");
    del1.DynamicInvoke(mt1arg2);
    del2.DynamicInvoke(mt2arg2);

    Debug.Log("\nInvoke:");
    del1.Invoke(mt1arg2);
    del2.Invoke(mt2arg2);
}

И вот результат вывода:

Target/1st arg of Method1 -> a
Target/1st arg of Method2 -> (1.0, 1.0)

Dynamic Invoke:
arg1(char) -> a   arg2(string) -> xyz
arg1(Vector2) -> (1.0, 1.0)   arg2(AuxSegment) -> (5.0, 0.0), (5.0, 10.0)

Invoke:
arg1(char) -> 揀   arg2(string) -> xyz
arg1(Vector2) -> (5.0, 0.0)   arg2(AuxSegment) -> (5.0, 10.0), (0.0, 0.0)

Почему в Method1 "char" переводится неправильно?Почему в Method2 есть странное переключение аргументов даже во втором аргументе?(arg1 равно arg2.p1, arg2.p1 равно arg2.p2, а arg2.p2 равно нулю) Я должен отметить, что это происходит для каждого подобного случая, который я пробовал.Есть ли возможное решение без использования DynamicInvoke, поскольку оно не очень эффективно?

Приносим извинения за неудобства и большое спасибо.

1 Ответ

0 голосов
/ 12 мая 2018

Я полагаю, что проблема заключается в том, что firstArgument (mt1arg1) и (mt2arg1) являются значениями типа param в ваших методах del1 и del2.

Согласно документам MSDN: https://msdn.microsoft.com/en-us/library/s3860fy3(v=vs.110).aspx

Если указано значение firstArgument, оно передается методу каждый раз, когда делегат вызывается; Утверждается, что firstArgument связан с делегат, и делегат, как говорят, закрыт в течение его первого аргумент. Если метод является статическим (Shared в Visual Basic), аргумент список, предоставляемый при вызове делегата, включает все параметры кроме первого; если метод является методом экземпляра, то firstArgument передается скрытому параметру экземпляра (представленному в C #, или мной в Visual Basic).

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

Должны работать следующие изменения:

public delegate T TDelegate<T, T1>(T1 arg1);
public delegate T TDelegate<T, T1, T2>(T1 arg1, T2 arg2);

    public class MyVector2
    {
        public int mp1;
        public int mp2;

        public MyVector2(int p1, int p2)
        {
            mp1 = p1;
            mp2 = p2;
        }

        public override string ToString()
        {
            return "(" + mp1 + "," + mp2 + ")";
        }
    }

    public struct AuxSegment
    {
        public MyVector2 p1;
        public MyVector2 p2;
        public AuxSegment(MyVector2 n1, MyVector2 n2) { p1 = n1; p2 = n2; }
        public override string ToString()
        {
            return "" + p1.ToString() + ", " + p2.ToString();
        }
    }

    public static bool Method1(string arg1, string arg2)
    {
        Debug.Log("arg1(char) -> " + arg1 + "   arg2(string) -> " + arg2);
        return true;
    }

    public static bool Method2(MyVector2 arg1, AuxSegment arg2)
    {
        Debug.Log("arg1(Vector2) -> " + arg1.ToString() + "    arg2(AuxSegment) -> " + arg2.ToString());
        return true;
    }

    public void main()
    {
        string mt1arg1 = "c";
        string mt1arg2 = "xyz";

        MyVector2 mt2arg1 = new MyVector2(1, 1);
        AuxSegment mt2arg2 = new AuxSegment(new MyVector2(5, 0), new MyVector2(5, 10));

        var mt1 = ((TDelegate<bool, string, string>)Method1).Method;
        var mt2 = ((TDelegate<bool, MyVector2, AuxSegment>)Method2).Method;

        var del1 = (TDelegate<bool, string>)Delegate.CreateDelegate(typeof(TDelegate<bool, string>), mt1arg1, mt1);
        var del2 = (TDelegate<bool, AuxSegment>)Delegate.CreateDelegate(typeof(TDelegate<bool, AuxSegment>), mt2arg1, mt2);

        Debug.Log("Target/1st arg of Method1 -> " + del1.Target);
        Debug.Log("Target/1st arg of Method2 -> " + del2.Target);

        Debug.Log("\nDynamic Invoke:");
        del1.DynamicInvoke(mt1arg2);
        del2.DynamicInvoke(mt2arg2);

        Debug.Log("\nInvoke:");
        del1.Invoke(mt1arg2);
        del2.Invoke(mt2arg2);
    }

Я использую String вместо Char и класс MyVector2 вместо Vector2, который является Struct. Другим вариантом может быть использование лямбды, например:

     TDelegate<bool, string> del1 = (u) => { return Method1(mt1arg1, u); };

    //or

    TDelegate<bool, string> del4 = (u) => 
    {
        var executed = (mt1.Invoke(this, new object[] { mt1arg1, u });
        if (executed != null && executed is bool)
        {
            return (bool)executed;
        }
        return false;
    };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...