Използване на параметри в делегат като базов клас

Имам делегат, който очаква параметри от тип A като параметри. Така че А е основният клас. Клас B и C наследяват от A.

Проблемът е, че въпреки че B и C наследяват от базовия клас A, функциите DoSomething в долната част на скрипта не могат да бъдат преобразувани в делегата.

public class A { }
public class B : A { }
public class C : A { }

public delegate void CallbackAction(params A[] paremeters);
public class Main
{
    public int main(params string[] args)
    {
        CallbackAction callbackAction;
        callbackAction = DoSomething1;
        callbackAction = DoSomething2;
        callbackAction = DoSomething3;

        return 0;
    }

    public void DoSomething1(A arg0) { }
    public void DoSomething2(B arg0) { }
    public void DoSomething3(C arg0) { }
}

Има ли някакъв начин да използвате параметри в делегат и да можете да използвате класове, които имат класа params като основен клас?

При компилиране грешката, която получавам, е: Грешка 5 Няма претоварване за „DoSomething3“ съответства на делегат „SKConsole.CallbackAction“

Използвам .NET 4 и XNA

РЕДАКТИРАНЕ:: Добре, позволете ми да обясня защо използвам това, създавам конзола. Това означава, че програмист, използващ моята конзола, може да добави команда (console.AddCommand("help", Help) към конзолата, Help тук е функция. Когато сте в играта и пишете help в конзолата, тя ще изпълни функцията Help() . Сега искам да работи и с console.AddCommand("setSpeed", SetPlayerSpeed). Функцията SetPlayerSpeed ​​има 1 параметър, int. Но искам да работи с всяка функция, така че ако програмист създаде функцията DoSomeFancyStuff(float a , низ b, int c) Искам конзолата да създаде команда и ако въведете правилния низ в конзолата, изпълнете тази команда.

Вече се опитах да направя много делегати за различни функции, но това е доста грозно според мен.

След това опитах следното

public abstract class SKConsoleParameter
{
    protected string value;

    public SKConsoleParameter(string value)
    {
        this.value = value;
    }
    public string GetRawValue()
    {
        return value;
    }

    public abstract bool IsValid();
    public abstract object GetValue();
}

public class StringParam : SKConsoleParameter
{
    public StringParam(string value) : base(value) { }

    public override bool IsValid()
    {
        return true;
    }

    public override object GetValue()
    {
        return value;
    }
}

public class IntParam : SKConsoleParameter
{
    public IntParam(string value) : base(value) { }

    public override bool IsValid()
    {
        int i;
        return int.TryParse(value, out i);
    }

    public override object GetValue()
    {
        int i;
        if (int.TryParse(value, out i))
            return i;
        else
            return 0;
    }
}

Това беше, че ако разработчик създаде функция като:

DoSomethingCool(StringParam s, IntParam i)

След това може да получи стойностите, като използва (string)s.GetValue() и (int)i.GetValue() Класовете StringParam и IntParam наследяват от SKConsoleParameter, така че реших, че сега мога да създам следния делегат

void CoolDelegate(params SKConsoleParameter[] parameters)

Но това не работи... Поради абстрактния проблем с клас A, B и C в горната част на тази страница

Някой има ли идеи за справяне с този проблем?


person Simon Karman    schedule 27.02.2013    source източник
comment
Каква грешка получавате? Можете ли да използвате .NET 3.5 или .NET 4? Обмисляли ли сте да използвате Action‹T› вместо Delegate?   -  person Glenn Ferrie    schedule 27.02.2013
comment
Грешката, която получавам, е Error 5: No overload for 'DoSomething3' matches delegate 'SKConsole.CallbackAction'   -  person Simon Karman    schedule 27.02.2013
comment
Публикувам код, който работи с помощта на Action‹T›...   -  person Glenn Ferrie    schedule 27.02.2013
comment
Действие‹B› callbackAction; callbackAction = DoSomething1; callbackAction = DoSomething2; callbackAction = DoSomething3; Изглежда, че работи, но ще работи само за DoSomething1 и 2. Така че работи от B до A, но не и от A до B (и C). Не може ли да работи обратното?   -  person Simon Karman    schedule 27.02.2013
comment
Добре, ако приемем, че актьорският състав работи, какво трябва да направи това: ((CoolDelegate)DoSomethingCool)(new IntParam ("1"), new StringParam ("foo"))?   -  person Anton Tykhyy    schedule 27.02.2013
comment
@AntonTykhyy Това, което трябва да направи, е да зависи от това какво прави функцията DoSomethinkCool с него, може би потребителят на моята конзола какво трябва да зададе стойността на бронята на същност в играта, така че проверява за низа foo на gameobject и ако намери един задава охранителя на 1. skConsole.AddCommand(setArmor, SetArmor); void SetArmor(StringParam entitie, IntParam armorValue) {   -  person Simon Karman    schedule 27.02.2013
comment
Пропуснахте смисъла на въпроса ми -- редът на двата аргумента е грешен, така че техните типове не съвпадат със сигнатурата на DoSomethingCool.   -  person Anton Tykhyy    schedule 27.02.2013
comment
@AntonTykhyy Прав си... Това няма да проработи. Мисля, че ако наистина искам това да работи, трябва да започна да използвам Reflection.. Благодаря за помощта!! И ще ви уведомя, когато го накарам да работи с помощта на отражение   -  person Simon Karman    schedule 27.02.2013


Отговори (3)


въз основа на вашия актуализиран код - опитайте това. Докато вашият метод съответства на делегата, контравариацията трябва да работи както се очаква в .NET 3.5 или по-добра

public abstract class SKConsoleParameter
{
    protected string value;

    public SKConsoleParameter(string value)
    {
        this.value = value;
    }
    public string GetRawValue()
    {
        return value;
    }

    public abstract bool IsValid();
    public abstract object GetValue();
}

public class StringParam : SKConsoleParameter
{
    public StringParam(string value) : base(value) { }

    public override bool IsValid()
    {
        return true;
    }

    public override object GetValue()
    {
        return value;
    }
}

public class IntParam : SKConsoleParameter
{
    public IntParam(string value) : base(value) { }
    public override bool IsValid()
    {
        int i;
        return int.TryParse(value, out i);
    }
    public override object GetValue()
    {
        int i;
        if (int.TryParse(value, out i))
            return i;
        else
            return 0;
    }
}   

class Program
{

    public delegate void CoolDelegate(params SKConsoleParameter[] parameters);

    static void Main(string[] args)
    {
        var s = new StringParam("Glenn");
        var i = new IntParam("12");
        var coolDel = new CoolDelegate(DoSomethingCool);
        coolDel(s, i);
    }

    public static void DoSomethingCool(params SKConsoleParameter[] parameters)
    {
        if (parameters == null) throw new ArgumentNullException("parameters");
        foreach (var item in parameters)
        {
            if (item is IntParam)
            {
                // do something interesting
                continue;
            }

            if (item is StringParam)
            {
                // do something else interesting
                continue;
            }

            throw new NotImplementedException("unknown param type");
        }
    }
}
person Glenn Ferrie    schedule 27.02.2013
comment
Благодаря! Това наистина е решение и вероятно ще го внедря по този начин, но съм готов също да опитам и да проверя дали с отражение (вероятно по-грозно) мога да го направя да работи малко по-гладко за потребителя. Ако работи, ще оставя и двата начина в програмата, така че потребителят сам да избере какво да използва. Благодаря още веднъж! - person Simon Karman; 28.02.2013
comment
Отражението има значително влияние върху представянето. Бих го избегнал в моите основни процедури и логика на приложението. - person Glenn Ferrie; 28.02.2013
comment
това е само в потребителския код, така че би трябвало да е голям проблем - person Simon Karman; 28.02.2013

Търсите грешката на грешното място. C# позволява противоречие във входните параметри на делегатите. Проблемът с вашия код е, че вашият делегат приема params A[], докато вашият метод взема едно A. Това не е позволено. Delcare вашия делегат като приемащ едно A:

delegate void Callback1 (B a) ;
void Test11 (A a) {}
void Test12 (B b) {}

Callback1 c11 = Test11 ; // OK
Callback1 c12 = Test12 ; // OK

Имайте предвид също, че това не работи с параметри на масива:

delegate void Callback2 (B[] a) ;
void Test21 (A[] a) {}
void Test22 (B[] b) {}

Callback2 c21 = Test21 ; // compile error
Callback2 c22 = Test22 ; // OK
person Anton Tykhyy    schedule 27.02.2013
comment
Има ли да се използват параметрите в делегат, без да се използва масив? Защото не знам броя на аргументите на функция. Това, което искам най-добре, е делагат, който приема всяка функция, възможно ли е това? - person Simon Karman; 27.02.2013
comment
Ако не знаете броя на аргументите, как ще го наречете? Какъв е вашият случай на употреба? Вероятно сте решили лесната половина на проблема и това, което остава, е невъзможната половина. - person Anton Tykhyy; 27.02.2013
comment
Не мисля, че това Callback1 c12 = Test12 се компилира. Входните параметри са контравариантни, но вашият пример се нуждае от ковариация. - person Henrik; 27.02.2013
comment
Добре, позволете ми да обясня нещо повече, създавам конзола. Това означава, че програмист, използващ моята конзола, може да добави команда (console.AddCommand(help, Help) към конзолата, Help тук е функция. Когато сте в играта и пишете help в конзолата, тя ще изпълни функцията Help(). I сега искам да работи и с конзолата.AddCommand(setSpeed, SetPlayerSpeed) също. Функцията SetPlayerSpeed ​​има 1 параметър, int - person Simon Karman; 27.02.2013
comment
@Henrik Грешката ми, поправена. @Simon Добре, откъде идва този параметър? От конзолната линия, предполагам? Как ще разберете как да го анализирате, т.е. кой тип е параметърът? Как проверявате броя на параметрите? Какво се случва, ако броят на параметрите е грешен или низовете не се анализират? Трябва да обмислите това. Има различни опции за изпълнение, нито една от които не е много красива. Най-простият и груб вероятно е MethodInfo.Invoke. - person Anton Tykhyy; 27.02.2013
comment
@SimonKarman също вашият въпрос не съвпада с вашето обяснение на случая на употреба, освен ако не считате A за object и B за int. - person Anton Tykhyy; 27.02.2013
comment
@AntonTykhyy в случая на използване A е SKConsoleParameter и B е IntParam и C е StringParam - person Simon Karman; 27.02.2013
comment
Защо да създавате такива интерстициални класове, вместо да използвате обикновени int, string и т.н.? Така или иначе трябва да обмислите въпросите в моя по-дълъг коментар по-горе. - person Anton Tykhyy; 27.02.2013
comment
Току-що го пробвах, така че потребителят на тази конзола може също да добави свои собствени класове като IntBetween0and100Param, така че този клас съдържа код, който гарантира, че въведената от потребителя стойност е между 0 и 100 - person Simon Karman; 27.02.2013
comment
Разбирам, това е разумно. Тъй като това е потребителски код, скоростта не е проблем. Трябва да използвате Reflection, за да анализирате сигнатурата на метода (MethodBase.GetParameters ()), да създадете необходимите аргументи от низове (Activator.CreateInstance) и да извикате метода (MethodBase.Invoke). Вашата AddCommand ще получи MathodInfo вместо делегата. - person Anton Tykhyy; 27.02.2013
comment
@AntonTykhyy Благодаря за помощта! - person Simon Karman; 28.02.2013

Опитайте този код:

 public class A {  }
public class B : A { }
public class C : A { }

public static class Helper
{
    public static Action<A> DoSomething;
}

class Program
{
    static void Main(string[] args)
    {
        var a = new A();
        var b = new B();
        var c = new C();

        Helper.DoSomething = new Action<A>(DoSomething1);
        Helper.DoSomething = (Action<A>)new Action<B>(DoSomething2);
        Helper.DoSomething = (Action<A>)new Action<C>(DoSomething3);                       
    }

    public static void DoSomething1(A a) { }
    public static void DoSomething2(B a) { }
    public static void DoSomething3(C a) { }
}
person Glenn Ferrie    schedule 27.02.2013
comment
Благодаря! Но не мисля, че това ще работи, ако броят на параметрите е променлив - person Simon Karman; 27.02.2013
comment
Разбира се, виждам накъде отиваш с това. - person Glenn Ferrie; 27.02.2013