C# Attributes (Predefined and Custom Attributes) - Advanced C# Tutorial (Part 8)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] hi and welcome to the eighth part of this advanced c-sharp course in this video we'll focus on the use of attributes in c-sharp for more content on advanced c-sharp concepts and much more please consider subscribing and please ring the bell so that you'll be notified of future content if you like this video at any point please consider giving it a thumbs up it will be greatly appreciated so let's discuss attributes attributes allow the developer to add metadata in a declarative way to program elements in code for example classes methods properties parameters etc a declared attribute is instantiated into an object at runtime and at compile time we can declare attributes against program elements to provide additional metadata to relevant program elements the developer is also able to declare global attributes against assemblies and in doing so add or modify metadata about the relevant assembly so the developer can use attributes to add or modify metadata about a particular program element or the assembly itself in this video we'll also briefly look at reflection reflection is a technology that we can use to read the metadata provided through the use of attributes at runtime we are then able to for example implement code logic based on the metadata provided through the use of attributes we'll look at how we can do this later in this video there are two broad categories for attributes predefined attributes and custom attributes predefined attributes are built into the baseclasslibrariesand.net in this video we'll look at two main types of predefined attributes global predefined attributes and general predefined attributes global attributes like for example assembly version attribute and assembly description attribute facilitate adding and modifying metadata for an assembly general attributes like for example conditional attribute obsolete attribute and attribute usage can be applied to appropriate program elements we'll start by implementing a code example using the assembly version attribute and assembly description attribute which are global attributes we'll use reflection to read the relevant assembly metadata and output the assembly metadata to the console screen we'll then implement a code example using the following general attributes conditional attribute obsolete attribute and attribute usage the attribute usage attribute will be declared on our own custom attributes that we'll create a bit later in this video we'll use custom attributes and reflection to implement field validation functionality we'll demonstrate how custom attributes can be created by creating classes that inherit from the built-in system.attribute class later in this video we are going to create custom attributes as part of validation logic that imitates how the asp.net mvc framework provides flexibility to developers through the use of reusable validation related attributes right let's create our code examples so that we can gain an understanding of attributes in c sharp let's create a dot net core console application project let's name this project attributes examples let's ensure that we are using.net 5 for this project if you haven't yet installed.net 5 please see a link below in the description where you are able to download the.net 5 sdk note that you'll mostly be able to follow along even using earlier versions of.net but it is advisable to download.net 5 to follow along with this tutorial and future tutorials one of my core aims is to keep these tutorials right up to date please view a video created for this channel about the significance of the release of dot net 5 a link to this video can be found below in the description so let's say that we wish to declare the assembly version global attributes in our code we are able to do this by declaring this global attribute between the namespace declaration and the using directives okay so why can't i see the assembly version attribute in the intellisense dropdown the reason is that the assembly version attribute is part of the system.reflection namespace so let's add a using directive to the system.reflection namespace and now we are able to see the assemblyversion attribute along with other global attributes in the intellisense drop-down list and we can now declare the global attribute against our assembly like this so here we are attempting to set our attribute version number to 2.0.1 through the declaration of the assembly version global attribute great so let's see what happens when we attempt to compile the code so we have a compile time error let's look at our error list to take a look at the details of this error the description of the error is duplicate system dot reflection dot assembly version attribute attribute so let's double click on the error item in our list that is causing the compile time error to occur okay so a new file is open and we can see a red squiggly line under the offending code it is a file that has a name that contains the assembly name followed by the suffix dot assembly info and we are able to find this file within our projects directory structure and if we look at the comments within this file it is clear that this file has been auto-generated it's in fact been auto-generated by ms build let's navigate to this url to read a bit about ms build the microsoft build engine is a platform for building applications this engine which is also known as ms build provides an xml schema for a project file that controls how the build platform processes and builds software visual studio uses ms build but ms build doesn't depend on visual studio by invoking msbuild.exe on your project or solution file you can orchestrate and build products in environments where visual studio isn't installed visual studio uses ms build to load and build managed projects the project files in visual studio dot cs proj.vbproj.vcxproj and others contain ms build xml code that executes when you build a project by using the ide visual studio projects import all the necessary settings and build processes to do typical development work but you can extend or modify them from within visual studio or by using an xml editor so the basic point i'm trying to make here is that many of these global attributes will be automatically declared in the assembly info file when compiling your code using the visual studio ide you can gain access to some of these global attributes through the visual studio ide if you try to declare a global attribute as i have attempted to here you may get a compile time error because ms build has automatically declared the global attribute in the assembly info file as we have seen this results in duplication of the relevant global attribute declaration which ultimately results in a compile time error occurring you are able to declare certain global attributes and the way we tried to declare the assembly version attribute for example let's declare the assembly description attribute let's comment out the assembly version attribute declaration let's add our own assembly description metadata to our assembly through a declaration of the assembly description global attribute let's pass the value of my assembly description to the constructor of the assembly description attribute note that depending on how the relevant attribute has been written when the developer declares an attribute the developer is able to provide custom metadata to the relevant attribute through the attributes exposed public properties or the relevant attributes constructor parameters so we know that the purpose of attributes is so that we can add or modify metadata against certain program elements or even against the assembly itself note that attributes on their own do not change application behavior the metadata provided through attribute declarations can be read at runtime through the use of reflection relevant metadata can then be evaluated by our code and appropriate code logic can be invoked based on the declared metadata so this is how the microsoft docs describes reflection reflection provides objects of type type that describe assemblies modules and types you can use reflection to dynamically create an instance of a type bind the types to an existing object or get the type from an existing object and invoke its methods or access its fields and properties if you are using attributes in your code reflection enables you to access them right so let's use reflection to access and read certain metadata that has been added through the use of global attributes so we need a using directive to the system.reflection namespace which has already been included in our code so let's write some code in our main method let's get a reference to the current assembly we can do this by getting a reference to the program classes type and then assigning its assembly property value to a variable we have named our variable this assam referring to the current assembly we can then get the relevant assembly name by using the assembly reference stored in the this assam variable and calling its get name method and we can access the assembly's version information through the version property now we want to read the assembly's description which should be the description that we included when we declared the assembly description global attribute to get a reference to the assembly description attribute we can call the get custom attributes method on the this same object and pass in the type of the attribute of interest to us note that we are using the type of method to get a reference to the object type of the relevant attribute remember attributes in c sharp are objects at runtime an object instantiated from the assembly description attribute class should be the first item in our object array we can use the as operator to cast the object to the assembly description attribute type if the casting operation fails null will be returned if the operation succeeds an object of the assembly description attribute type is returned let's write code to write out the metadata information about the current assembly that we have been able to retrieve through reflection to the console screen let's run the code great so we have looked at examples of global predefined attributes let's look at some general predefined attributes let's start with the conditional attribute let's first add a new standard library project to our solution and let's name our project logging component let's remove the automatically created class named class1 and add a new class named logging to our new project let's add a public static method to the login class named log to screen it contains one parameter named msg and is defined as a string the code for this method simply outputs the value referenced by the msg argument to the console screen let's declare the conditional attribute against the log to screen program element we have a red squiggly line because the conditional attribute is a member of the system.diagnostics namespace so let's add a using directive to the system.diagnostics namespace and now let's pass in an argument into the constructor of our conditional attribute let's pass in the text log underscore info so what does log underscore info mean log underscore info is a piece of text that i've made up this text represents a symbol in our code we can use the define preprocessor directive and include the log underscore info symbol in our code if the log underscore info symbol is defined using the preprocessor defined directive within a file containing method calls to the log to screen method the code within the log to screen method will be executed at run time however if the predefined processor directive is not used to define the log underscore info symbol in the file where the log to screen method is called its code will not be executed at runtime because we have used the conditional attribute to declare to the compiler that we would like this behavior to occur regarding the log to screen program element so the combination of the conditional attribute and the use of the defined preprocessor directive can act like a switching mechanism i.e using this mechanism we are able to switch certain code on or off this is especially useful for debugging purposes if this is not clear don't worry we will demonstrate what is meant here through a code example we are able to define our log underscore info symbol through the use of a preprocessor directive we are going to use the define preprocessor directive for this purpose preprocessor identifiers are used to help in conditional compilation if we navigate to this url we can see some of the preprocessor directives available in c-sharp for more information on preprocessor directives please use this section of the microsoft docs as your guide so here we have the hash defined preprocessor directive that we can use to define a symbol in our code so let's see how we can use a particular preprocessor directive in conjunction with the conditional attributes to conditionally output information to the console screen through our log to screen method we must add our appropriate preprocessor directive code right at the top of our program.cs file in our code editors like this to implement the desired functionality you can see we have the defined preprocessor identifier in this line of code as well as the log underscore info symbol that we made up and passed as text to our conditional attribute which was declared against the log to screen program element let's include a using directive to our login component namespace in our main method let's make a call to the logging.log to screen method and pass in the following text as an argument to the msg parameter this code is testing logging functionality let's run the code and as expected the appropriate text is written to the console screen so now let's see what happens when we comment out the relevant preprocessor directive code and run the code again and you can see that nothing has been outputted to the console screen using our preprocessor directive we can switch the execution of the code against which the relevant conditional attribute is applied on and off at compile time this functionality can be very useful for debugging purposes so let's say we have a change to our login component where we want all logging to be done to a text file rather than to the console screen and we want the consumers of our code to use the method that logs the information to a text file rather than to the console screen so how can we let the consumers know to use the log to file method rather than the log to screen method we can simply declare the obsolete predefined general attribute against the log to screen method let's create a public static method named log to file in the interests of time rather than writing the code to actually log the value passed into this method to a file let's just write code to output the value to the console screen and pretend that its method logs relevant text to a text file okay so let's declare the obsolete attribute against the log to screen method and let's pass a meaningful message to the constructor of the obsolete attribute so we want any calling code that is currently using the log to screen method to receive the following warning message the log to screen method has now been deprecated please use the log to file method instead so now if we go to our main method we can immediately see a green squiggly line under the code that is calling the log to screen method and if we hover our mouse pointers over the green squiggly line we can see that the text that we passed as an argument to the obsolete attribute is presented to us and note that if we want to force the user of our code to not use the log to screen method at all we can set a second parameter in the obsolete attribute to true if this value is set to true an error is flagged when a user attempts to use the deprecated method i.e the log to screen method now you can see that the green squiggly line has turned into a red squiggly line meaning that we no longer just receive a warning message we are receiving an error message this means we won't be able to compile the code the last predefined attribute that i wish to discuss is the attribute usage attribute we are going to create our own custom attributes and use reflection to read the relevant metadata added to relevant program elements through our custom attributes so reflection and attributes will be implemented to provide flexibility to developers using our custom attributes to declare how relevant fields should be validated this code is going to be very similar to the code that the asp.net mvc framework implements for field validation in asp.net mvc we can declare certain validation related attributes against relevant program elements and the mvc framework automatically handles validation of relevant fields for us behind the scenes as it were in this code example however we are going to create the custom attributes used for declaring how we want relevant fields validated and we will also write code using reflection to read the attribute data declared against relevant program elements in this case the program elements will be properties we will also write the validation code related to each of our custom attributes okay so let's add a new standard library project to our solution let's name this project validation component let's delete the class that was automatically created by visual studio let's create a new folder named custom attributes let's add our first custom attribute class to the custom attributes folder let's right-click the custom attributes folder and add a new class let's name this class required attribute note that we must include the attribute suffix and the class name of our custom attribute however when we declare our custom attribute you'll see that in the declaration we can omit the attribute suffix from the attribute name we are however allowed to include the attribute suffix in our attribute declaration but functionally this is unnecessary we'll demonstrate this a bit later the code for this attribute is very simple let's create a public auto implemented property of type string named error message let's add codes to a parameter list constructor whereby the error message property is set to a default value so let's say we want a default error message of you cannot leave field and then in curly brackets we have a zero followed by the word empty so the zero and curly brackets allows the relevant field name to be inserted into the message at a later time this is achieved through the use of composite formatting if you are not familiar with composite formatting regarding string values please view a tutorial created for this channel on c-sharp strings a link to this tutorial has been provided below in the description right let's create a parameterized constructor this will allow the developer to pass in a custom error message when declaring the required attribute against the relevant program element the declaration of the required attribute will be demonstrated a bit later in order for our class to be recognized as a custom attribute we must ensure that our class inherits from the built-in system.attribute class a custom attribute must inherit from the system.attribute class either directly as we are doing here or indirectly for example if our class inherited from another user defined class that inherits from the system dot attribute built in class so now all that is left to do is apply the predefined attribute usage attribute against our required attribute class like with the obsolete predefined attribute the attribute usage predefined attribute is a member of the system namespace basically the attribute usage predefined attribute allows us to provide metadata to the compiler that will tell the compiler which elements are allowed to have our predefined attribute declared against them so we can apply rules or constraints through the use of the attribute usage predefined attribute against our custom attribute at the class level this is an example of declaring an attribute against the class so in the first argument passed into the attribute usage attribute we can declare which program elements are allowed to be decorated with our custom attribute so if we have decided that we only want our custom attribute to be declared on the following program elements fields parameters and properties we can declare this by passing in this code to the first parameter of the attribute usage attributes constructor like this so here we are declaring which target program elements are able to be decorated with our required custom attribute we can optionally set a property of type boolean named allow multiple through our declaration if we set the allow multiple property to true this means multiple instances of our custom attribute can be declared against a single program element we only want one instance of our custom attribute to be declared against a single program element so let's set the allow multiple property to false we also have a property named inherited that we can optionally set through this declaration setting this property determines whether the relevant custom attribute is inherited by a derived class in this example we won't set this property and that's it we have now created our required custom attribute so let's create our next custom attribute let's add a new class file to our custom attributes folder and let's name this class string length attribute let's add three public auto implemented properties to our string length attribute class max length for the storage of an integer value denoting the maximum number of characters that a user can enter for a field related to a property against which the string length attribute has been declared error message for the storage of a string value denoting the message outputted to the user when the user has entered an invalid value into a field related to a property against which the string length attribute has been declared min length for the storage of an integer value denoting the minimum number of characters that a user can enter for a field related to a property against which the string length attribute has been declared so let's create a private method named set properties that we can reuse for setting our public properties this method will be called from a number of constructor overloads that we'll implement in a bit for this class this method does not return a value and contains three parameters that correlates to the public properties that we have already created the first parameter is max length which is defined as int the second parameter is error message defined as string the third parameter has been length defined as a nullable integer the question mark symbol following the end keyword denotes that this parameter is nullable so it is a nullable value type parameter because it's defined as an integer which is of course a value type let's also make our min length parameter optional we can do this in c-sharp by simply assigning the relevant parameter a default value within the method definition like this so if a value is not passed into this parameter by default the min length argument will have a value of null before we move on with our example let's go to the microsoft docs specifically to this location to read a bit about nullable value types a nullable value type t question mark represents all values of its underlying value type t and an addition of null value for example you can assign any of the following three values to a bull question mark variable true false or null an underlying value type t cannot be a nullable value type itself very basically a null value means that a value for the relevant variable has not been set adding a question mark at the end of the value type definition for a variable or a parameter means we have the option of storing a null value in the relevant variable or parameter note that optional parameters must be included at the end of the parameter list in the relevant methods method signature okay so let's implement the logic for the set properties private method so firstly let's check to see if the error message argument contains an empty string if it does we want to set a default error message value in order for our error message to contain appropriate text depending on whether the min length value is set we can create a nested if statement so if the min length value is null let's appropriately set our default error message if the min length value is not null let's include appropriate text relating to the min length value within our default error message so if the developer does not pass in an error message when declaring the string length attribute against an appropriate program element a default error message will be used for the scenario where an error message value is set when the relevant attribute is declared against a program element the error message provided must be assigned to the error message public property of the attribute then let's set the max length property this is a property value that the developer must pass in when declaring the string length attribute against an appropriate program element let's then assign the appropriate value to the min length property so let's use a ternary operator to check if the min length argument is null i.e has not been set by the developer declaring the string length attribute against an appropriate program element if the min length argument is null let's assign 0 to the min length property if the min length argument contains a value let's assign the value to the min length property so let's create our constructor overloads firstly let's create a constructor that contains just the max length parameter then within our constructor let's call our private set properties method to assign values to the appropriate public properties of the string length attribute class okay i also want to make the error message parameter in the set properties method an optional parameter we can do this by simply assigning default value to the error message parameter like this note that the parameter after the error message parameter in our parameter list is also the last parameter in the parameter list and is optional so it is perfectly legal to make our error message parameter optional remember the optional parameters must be included at the end of the relevant parameter list let's create a constructor overload that contains the max length parameter and the error message parameter then let's use our private set properties method to assign values to the appropriate public properties of the string length attribute class let's create constructor overload that contains the max length parameter and the min length parameter let's use our private set properties method to assign values to the appropriate public properties of the string length attribute class note how we are passing in a value to the min length optional parameter of the set properties method we need to be specific as to what parameter is being passed an argument here because in this case we are not passing in an argument to the error message parameter the fact that the error message parameter and the min length parameter are both optional means in cases like this we have to make it clear which parameter we want to pass our argument to let's create one more constructor overload this constructor overload contains the max length parameter the error message parameter and the min length parameter let's use our private set properties method one more time to assign values to the appropriate public properties of the string length attribute class let's ensure that we include the code whereby our class inherits from the system.attribute built-in class so now let's declare the attribute usage predefined attribute against our string length attribute class so we can copy the attribute usage declaration from our required attribute class and appropriately paste it in to declare the attribute usage attribute against our string length attribute class great so we have now completed two custom attributes the required attribute custom attribute and the string length custom attribute let's create one more custom attribute this attribute will be responsible for indicating when declared against a program element that the input field's value related to the relevant program element should match a specified regular expression pattern so regular expressions are great for defining patterns that an input field value must match for field types that must be constrained to a particular format like for example postcodes phone numbers credit card numbers email addresses etc so the validation code for fields like this will be simple if the relevant input field value matches the specified regular expression pattern the user's input is deemed as valid if however the input field value does not match the specified regular expression pattern the user's input is deemed as invalid we can implement the validation logic related to our custom attributes once we have completed the code for this final custom attribute so let's add a class to our custom attributes folder and let's name this class regular expression attribute let's create two public auto implemented properties the first property named error message is of the string data type the second property named pattern is also of the string data type the pattern property denotes the regular expression pattern that must be supplied by the developer when the regular expression custom attribute is declared against an appropriate program element let's create two constructor overloads the first constructor overload contains one parameter named pattern the developer is able to pass an appropriate regular expression pattern argument to this parameter when declaring the regular expression custom attribute against an appropriate program element the error message argument is not passed into this constructor but we can provide a default error message in this constructor and assign it to the error message property let's create a constructor overload this constructor contains two parameters one denoting the regular expression pattern and the other denoting the error message so this constructor allows the developer to pass an irregular expression pattern argument as well as a custom error message to a declaration when declaring this attribute against a program element so all we need to do within the constructor is assign the public property members of this class with the appropriate values passed into the relevant constructor as arguments let's ensure that we include the code whereby our class inherits from the system.attribute built in class the last step is to appropriately declare the attribute usage predefined attribute against the regular expression attribute class this declaration will be the same declaration applied against our other two custom attribute classes so let's copy the appropriate declaration from one of our other custom attribute classes and appropriately paste the contents of our clipboards into our regular expression attribute class file so now we have created three custom attributes implemented in three separate files the first custom attribute we created is named required attribute when this attribute is declared against an appropriate program element this means that the associated input field must not be left empty if it is left empty the field value is deemed as invalid the second custom attribute we created is named string length attribute when this attribute is declared against an appropriate program element this means the associated input field must contain a character length that falls within the boundaries defined by a specified minimum value and a specified maximum value the third custom attribute we created is named regular expression attribute when this attribute is declared against an appropriate program element this means that the associated input field must contain a value that matches a specified regular expression pattern so through the declaration of one or more of our custom attributes a developer can express how a certain input field should be validated without the logic to implement the validation rules however these attributes are useless these custom attributes perform no functionality other than to add metadata to relevant program elements so the next step is to write the validation code we must write the validation logic that correlates with each of our custom attributes through the use of reflection we'll be able to read the metadata added to the relevant program elements through the use of our custom attributes the use of our custom attributes is for the purpose of declaring how validation logic is related with relevant program elements which are also related to relevant input fields so let's write the relevant validation logic so to do this let's first add a new class named validation directly to the project node so we want this class to be separate from our custom attribute classes we are going to implement our own validation code in this class so we first want to create the validation logic that corresponds to each of our custom attributes the required attribute the string length attribute and the regular expression attribute let's encapsulate this functionality within three private methods let's first implement the validation logic that corresponds to the required attribute so let's create a private static method named field required is valid that returns a boolean value this method contains one parameter let's name this parameter entered value and define it as a string the logic for this method is basic if the entered value passed into this method as an argument is not null or empty return true because this means the user has entered a value which makes the input field entry valid if the code that returns true is not executed this means the entered value is invalid and false must be returned to the calling code let's implement the validation logic that corresponds to the string length attribute the method signature for this method is the same as the field required is valid method except we need to also pass in a field of type string length attribute to this method in addition to the parameter representing the entered value this is so that we can access the min length and max length public properties that we implemented for the string length attribute class the values stored in these two properties are integral to the validation logic performed against relevant inputted values let's name this method field string length is valid so to implement the validation code let's implement an if statement where the condition evaluates the entered value argument to see if its character length is greater than or equal to the min length property and is less than or equal to the max length property if the character length of the entered value argument falls within the range defined by the min length and max length property values the entered value is valid so we can return true to the calling code if however the character length of the entered value does not fall within the specified range we must return false because the entered value is not valid so let's now implement the validation private method that corresponds to the regular expression attribute so the method signature is almost the same as the field string length is valid methods method signature let's appropriately name this method field pattern match is valid instead of including a parameter of type string length attribute let's include a parameter of type regular expression attribute this is so that we can access the public property named pattern which is a member of the regular expression attribute class the pattern property will contain the regular expression pattern against which we need to validate the entered value so let's use the reg x class to perform our validation logic this class is a member of the system.txt.regularexpressionsnamespace so let's include an appropriate using directive the regex class contains a public static method named is match we can use this method to validate the entered value against the relevant regular expression pattern if the is match method returns true this means the entered value is valid so we must return true to the calling code if however the is match method returns false this means the entered value is not valid and we must return false to the calling code okay so we have now implemented the validation logic that corresponds to our custom attributes let's create a public static method named property value is valid that returns a boolean value this method is responsible for calling relevant validation code that is relevant to a specific program element and a user inputted value the validation code executed is determined by the custom attributes that have been declared against the relevant program element in the interests of keeping this code simple the code will only be relevant to a program element that is a property so the first item in the parameter list for the property value is valid method is a parameter of type type this parameter represents the type that contains properties against which our custom attributes may have been declared for example a user-defined type representing an employee record the next parameter represents the value that a user enters ie an input field which is correlated to a particular property and needs to be validated by our code this parameter is appropriately named entered value the next parameter represents the program element which will be a property in this case that is a member of the type represented in the first parameter so for example the program element could be the first name property which is a member of a user-defined type named employee the last parameter for this method represents the error message that will be displayed to the user if the user's input is deemed as invalid we want to output the error message value so let's include the out keyword in the relevant parameter definition let's implement the logic for this method firstly let's get a reference to the relevant program element so the program element in this example will be the property on which one or more of our custom attributes has been declared so for example if we are validating the first name property on the employee user-defined type the element name argument will contain the literal text first name which is the name of the first name property to get a reference to the user defines type in this case employee the calling code would pass in type of employee this means we can use reflection to inspect the relevant program element to ascertain which custom attributes have been declared against the relevant program element using reflection we can then ascertain how we should validate the relevant entered value so let's write code to get a reference to the relevant program element which in this case will be a property reflection can be used to get relevant information about a program element we want to know in our code what custom attributes have been declared against the relevant program element so we can use the property info type which is a member of the system.reflection namespace to gain access to the property information that we need we must include a using directive to the system.reflection namespace let's define a variable named prop as the property info type and let's assign a value returned from a call to a method named get property which is invoked on the type argument passed into the t parameter of this method note that we need to pass the element name argument to the get property method the element name argument will store a reference to the name of the relevant property we now have a reference to the relevant property information we can use the get custom attributes method to return a collection of custom attributes that have been declared against the relevant property we can assign the returned value to an array of type attribute the get custom attributes method is returning a collection of type i enumerable and i want to store a reference to the relevant custom attribute objects in an array so let's use links to array extension method to convert the i enumerable collection into an array let's initialize the error message output parameter to an empty string let's write code to perform a for each loop and loop through each attribute object declared against the relevant program element then within the for each loop let's implement a switch statement and use pattern matching code to get a reference to the relevant custom attribute objects so that we can perform appropriate validation on the entered value so the switch statement evaluates the type of each relevant attribute object using pattern matching using pattern matching we are able to get a reference to the attribute objects that have been declared against the relevant program element this is the magic of pattern matching in action pattern matching was introduced in c-sharp version 7. it allows the developer to write code to assess whether an object type matches the object type specified in a case statement if there is a match a reference to the relevant object is made available on the same line of the code as the case statement so here we have case required attribute if the object referenced by the attr variable is of type required attribute the ra variable is assigned the value referenced by the attr variable we are then able to use the ra variable in our validation code that resides within the relevant case block so we can call the appropriate private method for validating required fields that we implemented earlier to validate the relevant user entered value if the entered value is deemed as invalid we can output an error message to the user through the error message output parameter which has been appropriately marked with the out keyword meaning we can output a value through this parameter to calling client code if the relevant user input fails validation we need the property value is valid method to return false so let's include the other case statements pertaining to the other two custom attributes so let's implement the case block for the string length custom attribute and lastly let's implement the case block for the regular expression custom attribute great let's write the calling client code let's go to the dot net core console project let's start by creating classes that can represent data models the first model will be a class that represents an employee record so let's create a new folder and name it models let's add a new class named employee to the models folder this class contains six auto implemented properties id as int first name as string last name as string phone number as string email address as string and postcode as string great so we are now going to appropriately declare our custom attributes against the properties we have just added to the employee class these attribute declarations will indicate to our validation code how the input values associated with the employee classes relevant properties should be validated let's include an appropriate using directive at the top of our code to the validation component dot custom attributes namespace so let's apply the required attribute to the id program element notice how when we declare our custom attributes we don't need to include the attribute suffix so even though the class representing the required attribute is named required attribute when we declare the attribute against the relevant program element we can omit the attribute suffix so in this case we can declare the attribute using the name required let's declare the string length attribute against the first name property let's pass appropriate values to the constructor of the string length attribute so let's say the maximum character length for the first name input field should be 15. our next argument is the error message we wish to output to the user if the user enters an invalid value for the first name field for the last parameter let's say the user must enter a value for the first name field that has at least two characters let's declare the required attribute against the first name property so we can copy and paste the custom attributes declared against the first name property to the last name property now the phone number property will include the same attributes and settings applied against the first name and last name properties but we also want to validate the phone number against a regular expression pattern that can be used to validate uk phone numbers so let's navigate to this url i find this website very useful for finding regular expression patterns do a search for uk phone numbers let's copy an appropriate pattern from our search results we can then appropriately pass this pattern as an argument to our regular expression custom attribute so let's appropriately paste our pattern into our code note that we may need to include the at symbol preceding the string references of the regular expression patterns this is because the pattern may include backslash characters which need to be escaped because the backslash character is used by c sharp within strings for escape sequences for example the backslash n escape sequence denotes a new line within a string the backslash t escape sequence denotes a horizontal tab character within a string etc so by including the at symbol preceding the string we are indicating that any backslash character should be interpreted as a literal backslash character and not as part of an escape sequence so let's apply the custom attributes for the email address property let's find an appropriate regular expression pattern and appropriately paste it into our code let's do the same for the postcode address let's say we need the postcode to be a valid uk postcode great so now let's build a basic front end so that we can test our validation code let's write a reusable private method that contains logic to prompt the user for a relevant field value we need this code to run within an infinite loop which we can implement using a do loop once the user enters a valid value the code will break out of the infinite loop the get input method returns true once the user has inputted valid data for the relevant input field we can use the out keyword to make the last field and output parameter where we can pass the valid data to the calling code you let's go to the main method and implement the calling code let's instantiate an object of type employee let's ensure that we have a using directive to the attributes examples.models namespace because of course the employee class is a member of this namespace let's create three local string variables amp id first name and postcode we need to get a reference to the employee classes type we can do this using the type of method like this let's write codes to handle the user input for the employee's id so once the user has entered a valid value for this field the valid data can be assigned to the id property the id property of the employee type is an integer value so let's write codes to convert the relevant string value to an integer value let's write code to handle the user input for the employee's first name field once the user has entered a valid value for this field the inputted value can be assigned to the first name property let's write code to handle the user input for the employee's postcode so let's write code so that once the user has entered valid information for the relevant fields a message stating that the user has now entered valid data for the relevant employee is outputted to the console screen great let's test the code so if we press the enter key without entering an employee id we get an expected output an appropriate error message is outputted to the console screen let's enter a valid id great so if we leave the first name field blank and press the enter key we get an appropriate error message outputted to the console screen if we enter just one character for the first name field the string length validation code deems our input as invalid and an appropriate error message is output to the console screen so now if we enter a value for our first name field that contains a character length that falls within the character length range which is between 2 and 15 this input is deemed as valid if we leave the postcode field blank we are presented with an appropriate error message let's enter a valid uk postcode great just to demonstrate that we are able to reuse our custom attributes on other user-defined types let's create another model let's create a class to represent a department record let's apply some of our custom attributes let's go to our main method and test our code in the interests of time we'll only include the department short name field in our test here excellent so let's use a predefined attribute that already exists in a.net library so the attribute i want to use is named jsonignore so we want to add functionality to output our employee object to the console screen in json format using the json ignore attribute we can choose which fields to exclude from our employee object json output let's declare the json ignore attribute against the phone number property and the postcode property let's go to our main method and write the code to serialize the employee object in json format let's run the code and you can see that the json output does not include the phone number and the postcode fields great let's comment out the json ignore attribute declarations so that we can include all of our property values for the relevant employee object in our json formatted output okay so i accidentally inputted bob's surname which is jones for the postcode field and i've noticed that there is a bug in our code the error message format outputted to the screen is incorrect the validation was run correctly i.e clearly jones is not a valid postcode but the error message itself is incorrect the zero wrapped in curly brackets in the appropriate code should serve as a placeholder for the property name of the property against which the relevant attribute has been declared the one wrapped in curly brackets should serve as a placeholder for the regular expression pattern specified for the purpose of validating that a postcode entered by the user is in a correct uk postcode format so i can see what is causing this issue we need to remove the dollar symbol preceding the error message specified in this constructor we want to use composite string formatting and not string interpolation for this string value for details on composite formatting versus string interpolation please view a video on c-sharp strings created by this channel a link to this tutorial is available below in the description so let's repeat the input that we specified in the previous test great the validation code is correct and the outputted error message is also correct okay but in our outputted json text here the postcode field is null so it looks like we have another bug here oh right so when i copied and pasted the code here i forgot to change the relevant code so that the postcode property is set to the user's input okay let's correct this and let's test the code again excellent we looked at how attributes are used in code to add metadata to program elements like classes methods properties etc and reflection can be used to read the metadata added through the use of the attributes at runtime we discussed that there are two broad categories in c sharp for attributes namely predefined attributes which are built into the dot net base class libraries and custom attributes which allow the developer to implement the developer's own attributes by creating a class that inherits from the system dot attribute built in class either directly or indirectly we created code examples using predefined global attributes namely assembly version attribute and assembly description attribute this demonstrates that how global attributes can be used to add and modify metadata at the assembly scope we created code examples using predefined general attributes namely conditional attribute obsolete attribute and attribute usage attribute this demonstrated how we can add and modify metadata for program elements we then created our own custom attributes as part of code logic used for the purpose of field validation the use of reflection was also employed as part of this code logic this code implementation imitates how the asp.net mvc framework uses attributes for field validation i hope you've enjoyed this video on using attributes in c sharp please consider subscribing for more content on advanced c-sharp topics and much more and please ring the bell so that you'll be notified of future content if you like this video please give it a thumbs up it'll be greatly appreciated please feel free to share this video with anyone you feel may benefit from its content i really enjoy reading your comments so please feel free to engage with me in the comments section as always the code created for this video can be downloaded from github a link to the relevant github repository is available below in the description thank you and take care [Music] you [Music] [Applause] [Music] oh [Music] wow you
Info
Channel: Gavin Lon
Views: 2,642
Rating: 4.8202248 out of 5
Keywords:
Id: JOM6zDb9Wa8
Channel Id: undefined
Length: 75min 17sec (4517 seconds)
Published: Mon Jan 18 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.