Loading...
AntyfrizZz avatar AntyfrizZz 238 Точки

Activator.CreateInstance thrown Exception

Здравейте,

 

EDIT:

Условие + Авторко решение

Моята имплементация

 

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

IExecutable command = (IExecutable) Activator.CreateInstance(type, args);

 

Абстрактният клас Command изглежда така

 public abstract class Command : IExecutable
    {
        protected string[] arguments;
        protected IDatabase database;

        protected Command(string[] arguments, IDatabase database, int paramethersCount)
        {            
            this.arguments = arguments;
            this.database = database;
            this.ValidateParametersCount(paramethersCount);
        }

        public abstract string Execute();

        private void ValidateParametersCount(int count)
        {
            if (this.arguments.Length != count)
            {
                throw new InvalidOperationException(Constants.InvalidCommand);
            }
        }
    }

 

Има изискване, ако на входа са подадени различен по брой параметри от необходимите, да се хвърли грешка "Invalid Command". Всеки клас, наследник на този, има константно поле с броя на аргументи, които очаква. Подава го на този конструктор и при извикване на конструктора се извиква метода ValidateParametersCount, който сравнява броя на подадените аргументи с броя на очакваните. Ако са различни хвърля InvalidOperationException. Обаче, понеже Activator-а не е успял да инстанцира, той си хвърля друга грешка TargetInvocationException и до Catch-а стига нейното съобщение, а не това на IvalidOperationException.

 

В момента решавам проблема по следния начин, но не ми харесва.

catch (TargetInvocationException)
{
    this.userInterface.WriteLine(Constants.InvalidCommand);
}

 

Въпрос 1. Ок ли е в конструктора на абстрактния клас да правя проверка за броя на аргументите и ако не са равни да хвърлям грешка

Въпрос 2. Как да докарам до catch-а InvalidOperationException, а не TargetInvocationException

 

EDIT: Използвам поста да попитам и защо джъджа не ми харесва решението и гърми с "Compile time error"

Compiled file is missing. Compiler output: C:\Program Files (x86)\MSBuild\14.0\bin\Microsoft.Common.CurrentVersion.targets(1098,5): error MSB3644: The reference assemblies for framework ".NETFramework,Version=v4.5" were not found. To resolve this, install the SDK or Targeting Pack for this framework version or retarget your application to a version of the framework for which you have the SDK or Targeting Pack installed. Note that assemblies will be resolved from the Global Assembly Cache (GAC) and will be used in place of reference assemblies. Therefore your assembly may not be correctly targeted for the framework you intend. [C:\Windows\TEMP\kkupuoqr.fxr\Air Conditioner Testing System_Skeleton\BigMani\BigMani.csproj]

 

Поздрави!

Тагове:
1
C# OOP Basics 19/08/2016 12:26:16
kaloyannikov avatar kaloyannikov 531 Точки

Ами вариант е според мен тая валидация да се прави в CommandInterpreter-a ,той от името му идва че интерпретира командата т.е ако има грешни параметри както в случая може да има, да се прави тази логика в него.

Така че , ако го изнесеш там при intrepretCommand на команда първо ще правиш this.validateParams() ; обградено в try/catch и ако продължи в try-я ще execute-не командата, ако не ще принтира съобщението за невалиден брой параметри. По този начин дори можеш да не подаваш на всяка команда count . Така го виждам аз ,но предполагам ,че има и по-добро решение.

0
AntyfrizZz avatar AntyfrizZz 238 Точки

Здравей,

 

Команд интерпретъра знае само какъв тип команда да върне в зависимост от инпута. Той не знае коя команда колко параметъра очаква и ако искам да разбере, трябва да набия if else, но не ми се иска да го правя.

 

Поздрави!

0
kaloyannikov avatar kaloyannikov 531 Точки

Ами от кода който си дал разбирам , че първо сетваш arguments , след това db , и след това валидираш параметрите.

Т.е това нещо за arguments.length != parametersCount ,защо не може да е метод на CommandInterpreter?

A дори можеш чрез DependencyInjection да направиш , така че всяка команда да е с празен конструктор и да речем от CommandInterpreterа да сетваш необходимите депендънсита на всяка команда ( не знам дали всички имат еднакви) .

Но дори всички да имат еднакви , да речем в бъдеще се появява нова команда ,която ще има dependеncy само от Database ще взима нещо от там  и ще го принти. 

Това го има в лаба обяснено може да го видиш какво точно имам предвид. 

Поздрави.

0
15/08/2016 20:30:37
AntyfrizZz avatar AntyfrizZz 238 Точки

Здравей,

 

Т.е това нещо за arguments.length != parametersCount ,защо не може да е метод на CommandInterpreter? - Защото parametersCount е константа от клас наследник. Конструктора на клас наследник изглежда така

private const int ArgumetsCount = 2;

public FindAirConditionerCommand(string[] arguments, IDatabase database) 
    : base(arguments, database, ArgumetsCount)
{
}

 

Ето така ми изглежда CommandInterpreter-а. Знам, че не е най - добрата имплементация, но си ми е нотка за подобрение на кода. Предложи къде в него да проверя коя команда колко аргумента да очаква.

var commandPrefix = "BigMani.Core.IO.Commands.";
var commandSufix = "Command";
var commandFullName = commandPrefix + commandName + commandSufix;

var args = new object[] { commandArgs, this.database };

var type = Assembly.GetExecutingAssembly().GetType(commandFullName);

if (type == null)
{
    throw new InvalidOperationException(Constants.InvalidCommand);
}

IExecutable command = (IExecutable)Activator.CreateInstance(type, args);
this.injector.Inject(command);

return command;

 

Всеки клас си държи константа колко параметъра очаква. Ако реша да искарам тази информация извън класовете, трябва да използвам if else. if еди кой си клас, провери броя на параметрите. If друг клас... Ако се сещаш за друг начин, може да го споделиш.

 

Колкото до Dependency Injection-а,. Инжекта не е решение на представеня проблем. Всички команди изполват database, за това няма смисъл да я инжектвам. Ползвам инжект за една единствена команда.

 

Поздрави!

0
15/08/2016 21:02:42
Ivailo_Kodov avatar Ivailo_Kodov 97 Точки

А да те питам, как го оправи това с тоя compile error ? На мен джъджа ми хвърля такъв, на Recycling station задачата. Качвам и авторското рещение и пак дава :))) Малко наполовина встрани от темата  :P

0
AntyfrizZz avatar AntyfrizZz 238 Точки

Здравей

 

Оправи се, като зададох Target framework на проекта да е .NET framework 4.6.1. Не знам защо се оправи, но се оправи.

Properties на проекта >> Application и там ще видиш едно падащо меню.

 

Поздрави!

1
Ivailo_Kodov avatar Ivailo_Kodov 97 Точки

Благодаря :) Оправи се ! Само това не бях опитал, защото пишеше за версия 4.5.2, а си бях на нея :) 

0
stambi4a avatar stambi4a 126 Точки

    Здравей.

    Според мен можеш да си пратиш константите за броя на параметрите в класа Constants, като за всеки климатик сложиш отделна константа, която се използва за валидиране в injector-a или сложиш ValidateParametersCount в CommandInterpreter.

    По въпрос 1, един клас не би трябвало да се самовалидира, ако това не става в  пропъртита или използвани от тях методи. Всяка друга логика се счита че трябва да се валидира отвън, като допълнително изискване към поставената задача - domain logic. Една дискусия по въпроса. Така, че в случая мисля е по-добре да си направиш пропърти.

    По въпрос 2. Къде ти хвърля TargetInvocationEception? Аз разбирам, че го хвърля още при създаването на команди, макар, че според мен рефлекшъна там е ок. Ако все пак го хвърля там, значи имаш проблем с даденият рефлекшън. Ако ти го хвърля при създаване на климатик, значи или си направил грешна валидация за параметри(грешен брой), или другият рефлекшън ти е проблемен. При всички случаи не би трябвало да се стига дотам да хвърля даденият targetinvocationexception.

    Относно Edit-a, без целият код може само да се гадае, но няма да стрелям далеч от целта ако кажа, че ако разрешиш предишните въпроси ще разрешиш и този.

    Още нещо. Защо ти е да инжектваш нещо в конструктора на команда след като всяка команда е еднодневка. Ако изпълняваш командите в commandInterpretera, можеш директно да подаващ string[] arguments и db като параметри.

    Поздрави.

   

1
19/08/2016 03:35:54
AntyfrizZz avatar AntyfrizZz 238 Точки

Здравей,

 

В началото на поста поставих линк към условие, авторско решение и моето решение. Може би трябваше да го направя по - рано.

Както ще видиш в авторското решение, поставени са константи коя команда колко параметъра да очаква. Хубаво, но командите в него се изпъляняват със switch case, което намалява възможността за преизползваемост на кода. Реално в моята имплементация, ако искам да направя валидацията на очакваните параметри извън Comman класа, няма да има нужда от рефлекшъна и при добавяне на нова команда трябва да добавям нов case в switch-а.

По въпроса с пропъртито. Направих го с пропърти, но в моя случай то не се различава много от метода, който изтрих. Реално това пропърти не му трябва гетър, а само един private сетър, който върши работата на метода.

TargetInvocationException хвърляше, когато се опита да създаде команда на която са дадени различен брой от очакваните параметри. Activator.CreateInstance влизаше в конструктора, метода, който проверяваше за валиден брой параметри хвърля InvalidOperationException и поради хвърлената грешка, Activator.CreateInstance не успява да направи инстанция на обект и си хвърля негова грешка. Та до catch тялото стигаше TargetInvocationException. Беше предложено решение да използвам InnerExceptionMessage, което реши проблема. Ако искам рефлекшъна да не хвърля TargetInvocationException, трябва да валидирам параметрите преди да ги подам, което както казах по - горе, не искам да правя.

Edit-а се оправи, като зададох Target framework на проекта да е .NET framework 4.6.1. Не знам защо се оправи, но се оправи.

Колкото до inject-а, всяка команда използва параметри и database. Само една единствена използва един tester, та за нея позлвам inject. За нищо друго.

 

Като обобщение. Струва ли си switch/if за валидация на броя параметри и да не се използва рефлекшът? За нашия случай - ок. 10 команди, ще го преживеем. Но винаги трябва да се програмира с мисълта за по - лесно допълване на кода. Утре клиента ако изиска още 100 команди? Какво правим?

 

Благодаря за отделеното време!

 

Поздрави!

0
stambi4a avatar stambi4a 126 Точки

Отговорите на двата въпроса бяха малко принципни.

Сега като видях кода разбирам, че всъщност имаш 2 опции:

1.Оставяш TargetInvocationReflection. В този случай нямаш контрол над exception-a, nо това не е фатално понеже имаш string[] arguments и db и при всички положения той се хвърля заради грешен брой параметри.

2.Слагаш анотация над командите с пропърти броя параметри, променяш reflection-a да работи така:

                var type =
                this.commands.Values.FirstOrDefault(
                    typ =>
                    typ.GetCustomAttributes(typeof(CommandAttribute))
                        .Any(attr => ((CommandAttribute)attr).ParametersCount == commandArgs.Length));

                if(type == null)
                {
                    throw new InvalidOperationException(Constants.InvalidCommand);
                }

Така ако type  е null хвърляш InvalidOperationException.

Използваният Attribute има пропърти ParametersCount което може да е константа в наследниците. Може да изглежда така:

[AttributeUsage(AttributeTargets.Class)]
    public abstract  class CommandAttribute : Attribute
    {
        protected CommandAttribute(int parametersCount)
        {
            this.ParametersCount = parametersCount;
        }

        public int ParametersCount { get; }
    }

3.Предполагам, че има и други начини с рефлекшън, въпрос на занимавка.

Относно въпроса  дали си струва switch/if, ами той нарушава open-closed принципа, тоест не е кпк.

Като цяло решението ти е доста добро, при проверка предполагам, че това ще помогне да се "недогледат" малки неща като тази проверка.

Поздрави.

1
AntyfrizZz avatar AntyfrizZz 238 Точки

Здравей,

 

Идеята с атрибутите ми допада, но има един доста голям недостатък. Ако не се лъжа, начинът, който предлагаш хваща първатавата срещната команда, която има нужният брой аргументи. Може да имаме повече от една команда, която да изисква съответен брой аргументи.

 

Поздрави!

1
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.