I have my dll, it contains function:
function GetPdfReport(
//this is string representation of MyEnum
AStringParam : Pchar
): TByteDynArray; stdcall; export;
var
//my vars
begin
try
try
//i try to pass incorrect string value on purpose to get exception
MyEnumVariable := TRttiEnumerationType.GetValue<MyEnum>(AStringParam);
//code hide
except
on E : Exception do
begin
//log error
Log.Error(E.ClassName + ' : ' + E.Message, 'errors');
end;
end;
finally
//dispose
end;
Then i get exception:
The InnerException message was 'Invalid enum value '_24170' cannot be
deserialized into type
'MyEnum'.
I want log exception message with string value that i passed as parameter but not some unclear numbers like '_24170'. How can I do this?
Update:
Let's say i have MyEnum with 3 values (One, Two, Three), and when i pass to my function string "Five" i want to see exception like this:
Invalid enum value 'Five' cannot be deserialized into type 'MyEnum'.
Thanks.
The code that you present does not raise an exception in case the supplied text does not match one of the enum values. Instead a value of -1 is returned. Of course, -1 is not a valid enum value so that makes TRttiEnumerationType.GetValue a rather questionable method if you wish to perform error handling.
You would need to test for this yourself. Rather than using TRttiEnumerationType it might be simpler to go directly to GetEnumValue, which returns an integer and so makes error checking a little simpler to write.
var
OrdinalValue: Integer;
Value: MyEnum;
....
OrdinalValue := GetEnumValue(TypeInfo(MyEnum), AStringParam);
if OrdinalValue = -1 then
// handle error
Value := MyEnum(OrdinalValue);
Naturally you'd want to wrap this in a method to make it re-usable.
Related
TServiceData = class
strict private
FPriority: Integer;
...
public
property Priority: Integer read FPriority write FPriority;
...
end;
TMonthData = class
strict private
function GetServiceData(AIdx: Integer): TServiceData;
public
...
property ServiceData: TServiceData read GetlstServiceData; // incompatible types - Why?
end;
Sorry. Typing error. Original
property ServiceData: TServiceData read GetServiceData;
Because you do not have argument in the property.
property ServiceData: TServiceData read GetlstServiceData;
See - the GetlstServiceData here can have no arguments because Delphi has nowhere to take them from its only available information source at compile type - the very property ServiceData: TServiceData declaration.
You should either add the argument to the property or to remove it from the function.
TMonthData = class
strict private
function GetValue0Args(): TServiceData;
function GetValue1Arg(const AIdx: Integer): TServiceData;
function GetValue2Args(const AIdx: Integer; const Flavour: string): TServiceData;
public
...
property Data0Args: TServiceData read GetValue0Args;
property Data1Arg[ SlotNumber: integer ]: TServiceData read GetValue1Arg;
property Data2Args[ SNum: integer; Recipient: string ]: TServiceData read GetValue2Args;
end;
See so called "array properties": http://docwiki.embarcadero.com/RADStudio/Seattle/en/Properties#Array_Properties
Though "array properties" are not actually arrays. The term was poorly selected: arrays would never take complex entities like strings or objects as their index while properties and functions do take them as their possible arguments.
I have these codes:
TAPPUGroup = (APP_UG_USERS, APP_UG_SUPER_USERS, APP_UG_ADMINS);
TAPPUGroups = set of TAPPUGroup;
TAppUser = record
UID: integer;
UName: string;
UGroup: TAPPUGROUPS;
end;
...
LoggedUser: TAppUser;
I used include to add groups to LoggedUser.UGroup, now how I know the index of specific value in TAPPUGroup for example if APP_UG_SUPER_USERS included in LoggedUser.UGroup how I can get it's index in TAPPUGroup ?
Example: If LoggedUser.UGroup = APP_UG_SUPER_USERS then I want to return 1 if LoggedUser.UGroup = APP_UG_ADMINS I want to return 2 and so on.
If you really do want the index of a given enumeration item in the enumeration, all you need to do is just use Ord().
To go the other way, you can use the enumeration name as it it were a function:
AGroup := TAPPUGroup(1);
Anyway, Ord() is how you find the index of a given enumeration value (like APP_UG_USERS) in a contiguous enumeration declaration. To find out whether a particular set instance contains a given set element, ou use the "if xxx in ..." construct Remy shows, e.g.
if APP_UG_USERS in MySet then ...
You can also do this
var
AValue : TAPPUGroup;
MySet : TAPPUGroups ;
for AValue:= Low(TAPPUGroup) to High(TAPPUGroup) do
if AValue in MySet then ...
You don't need the index. To know if a value exists in the Set, use the in operator instead:
if APP_UG_SUPER_USERS in LoggedUser.UGroup then
I can declare the type of a class by declaring:
type
TMyObject = class(TSomething);
TMyObjectClass = class of TMyObject;
I'm trying to do something like this:
IData<TIn,TOut> = interface;
IData = interface
function GetGenericData<Tin,TOut>: IData<TInput,TOut>;
function GetInType: TRttiType;
function GetOutType: TRttiType;
end;
Declare a base type that can give access to the generic type and give me info on what the generic Input and Output types are.
IData<TIn,TOut> = interface(IData)
['{5B402458-22EC-4A8B-83F3-C11AC575B79E}']
function GetInput(Index: Integer): TIn;
function GetInputCount: Integer;
function GetOutput(Index: Integer): TOut;
function GetOutputCount: Integer;
property Input[index: integer]: TIn read GetInput;
property Output[index: integer]: TOut read GetOutput;
property InputCount: integer read GetInputCount;
property OutputCount: integer read GetOutputCount;
end;
Here is the actual generic type that holds an 2 arrays of data. Note that the input and output does not need to be the same type.
I then have a class that contains one or more different types of IData.
TStage = class(TPipelineStage, IData);
TStage<TIn, TOut> = class(TStage, IData<TIn, TOut>);
TMyPipeline = class
fStages = TArray<TStage>; <<-- this is really a list of TStage<?,?>
I want to have a list of different TStages in my class, but obviously I cannot code that.
How do I access the TStage<IInterfaceA, InterfaceB> and TStage<InterfaceB, InterfaceB> from fStages?
Can I use class of .... in this context and instantiate an object from there?
The reason I'm using this approach is that I'm using a delegate in the Pipeline stage that is declared thus:
TDelegate<TIn, TOut> = reference to procedure(const Data: IData<TIn, TOut>);
There are a limitation in Delphi language that will prevent you to do things exactly the way you are trying. Take the code:
IData<TIn,TOut> = interface;
IData = interface
function GetGenericData<Tin,TOut>: IData<TInput,TOut>;
function GetInType: TRttiType;
function GetOutType: TRttiType;
end;
Here you have a generic method in a interface. Delphi (at least until XE3) does not support generic methods in interfaces.
In other snipet you have:
TStage = class(TPipelineStage, IData);
TStage<TIn, TOut> = class(TStage, IData<TIn, TOut>);
TMyPipeline = class
fStages = TArray<TStage>; <<-- this is really a list of TStage<?,?>
The answer for the question in code is No. That´s an array, not a list. However, since your array is holding instances of TStage, it will also hold instances of TStage<TIn, TOut> with no problem, no matter what types are TIn and TOut.
You have this question:
I want to have a list of different TStages in my class, but obviously I cannot code that.
How do I access the TStage and TStage from fStages?
Actually, you can! It´s possible to declare a list of a certain type (TStage in your case) and hold actual instances of descending classes, even a generic subclass.
It seems to me that you want a generic method to return the TStage instance already typed to a descending type, in order to avoid a type cast and some type tests, eventually returning nil if the stage is an instance of another class, right?
In this case, a generic method can be helpful, but only in classes, not in interfaces. I would do something like this (I didn´t have Delphi at hand to test the code, but the idea seems to fit your needs):
interface
type
TMyPipeline = class
private
FStages: TObjectList<TStage>;
public
// Constructors, destructor and other methods...
function StageAs<TIn, TOut>(aIndex: Integer): TStage<TIn, TOut>;
end;
implementation
function TMyPipeline.StageAs<TIn, TOut>(aIndex: Integer): TStage<TIn, TOut>;
var
stage: TStage;
begin
stage := FStages[aIndex];
if stage.InheritsFrom(TStage<TIn, TOut>) then
Result := TStage<TIn, TOut>(stage)
else
Result := nil;
end;
I want to create a Pointer to a Class named "CustomParam", so I declared
pCustomParam = ^CustomParam
The Class CustomParam got following class variables, which should be set to 0 in constructor:
var keyArray: array of String;
var valueArray: array of String;
var paramArray: array of pCustomParam;
var isParamArray: array of Boolean;
var size: Integer;
The Constructor looks like that:
constructor CustomParam.create;
begin
inherited;
size:= 0;
SetLength(keyArray,0);
SetLength(valueArray,0);
SetLength(isParamArray,0);
SetLength(paramArray,0);
end;
and was declared like that:
constructor create; overload;
Now I try to create the Pointer to CustomParam with "new" like following:
var pointerToCustomParam: pCustomParam;
begin
new(pointerToCustomParam);
But it don't jump to constructor of CustomParam Class. If I call the constructor manually like following:
pointerToCustomParam^.create;
The application will crash on SetLength commands.
What I noticed is, that the variable "pointerToCustomParam" got rubbish content direct after the "new" function.
I hope you are able to help me and the information are enough :)
Thank you :)
The proper way to create an instance of a type is to call the constructor on the type and assign the result to a variable of that type:
var
Param: CustomParam;
Param := CustomParam.Create;
Instances created that way are already references, so there's rarely a need for an additional pointer.
If you really must have a pointer, then start by declaring the type:
type
PCustomParam = ^CustomParam;
Then declare a variable:
var
Param: PCustomParam;
Allocate memory for the contents of the thing it points to:
New(Param);
That doesn't necessarily assign a valid value to the CustomParam reference it points to, but if it does, is assigns the value nil. So finally, assign a value to that newly allocated memory:
Param^ := CustomParam.Create;
Notice how we still have to call the constructor, and we never call the constructor on the object we're creating, because that object doesn't exist until after the constructor is called.
Your problem is that your declaration
pCustomParam = CustomParam
defines pCustomParam to be of type CustomParam, not a POINTER to Customparam.
Hence pointerToCustomParam will not be a POINTER, but a CustomParam - hence the 'rubbish contents'
Try
pCustomParam = ^CustomParam;
In web application, I am using LINQ to call a procedure, the procedure is parameter procedure. but when I am passing arguments it is giving errors, This is my code:
MyLinqsDataContext DataContext=new MyLinqsDataContext ();
int eno=Convert.ToInt32 (txtempno.Text );
int dep=Convert .ToInt32 (txtDep.Text );
var sqr = from qr in DataContext.USP_Insert_Emp(eno, txtName.Text, dep)
select qr;
But is giving error like:
Could not find an implementation of the query pattern for source type int. Select not found.
This is my Proc :
create procedure USP_Insert_Emp(#empid int,#ename varchar(60),#deptid int)
as
begin
insert into Emp (empid ,ename,deptid ) values (#empid ,#ename ,#deptid)
end
DataContext.USP_Insert_Emp returns an int.
The error you are getting is because you are trying to call Select on an int and not an IEnumerable<T>.