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]
Поздрави!
Здравей,
В началото на поста поставих линк към условие, авторско решение и моето решение. Може би трябваше да го направя по - рано.
Както ще видиш в авторското решение, поставени са константи коя команда колко параметъра да очаква. Хубаво, но командите в него се изпъляняват със 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 команди? Какво правим?
Благодаря за отделеното време!
Поздрави!
Отговорите на двата въпроса бяха малко принципни.
Сега като видях кода разбирам, че всъщност имаш 2 опции:
1.Оставяш TargetInvocationReflection. В този случай нямаш контрол над exception-a, nо това не е фатално понеже имаш string[] arguments и db и при всички положения той се хвърля заради грешен брой параметри.
2.Слагаш анотация над командите с пропърти броя параметри, променяш reflection-a да работи така:
Така ако type е null хвърляш InvalidOperationException.
Използваният Attribute има пропърти ParametersCount което може да е константа в наследниците. Може да изглежда така:
3.Предполагам, че има и други начини с рефлекшън, въпрос на занимавка.
Относно въпроса дали си струва switch/if, ами той нарушава open-closed принципа, тоест не е кпк.
Като цяло решението ти е доста добро, при проверка предполагам, че това ще помогне да се "недогледат" малки неща като тази проверка.
Поздрави.
Здравей,
Идеята с атрибутите ми допада, но има един доста голям недостатък. Ако не се лъжа, начинът, който предлагаш хваща първатавата срещната команда, която има нужният брой аргументи. Може да имаме повече от една команда, която да изисква съответен брой аргументи.
Поздрави!
Ами да, моя грешка. Когато атрибута и командата имат обща част от името(което си зависи от теб) можеш да добавиш тази валидация, например.
Примерно: RegisterCarAirConditionerCommand - RegisterCarAirConditionerCommandAttribute
Друг вариант: добавяш пропърти с името на командата в атрибута.
Не съм тествал за всички команди, но примерно за RegisterStationaryAirConditionerCommand работи.
Благодаря за идеята с атрибутите. Изглежда ми като по - добрия подход.