Introduction
A Blazor Component can exist as either a standalone component or an entire page with its own route. When annotating properties in a Blazor Component with [Parameter], it allows us to handle incoming parameters with ease. For instance, when we use the [Parameter] attribute, we can accept custom parameters in our component like this.
<MyComponent MyCustomParameter="value" />
Or like this,
https://<host>/MyComponent/value
The Anatomy of a Blazor Component
When we look at the source code for a Blazor Component, we can immediately tell whether or not the component was intended to be used as a standalone page. If it is, then we know right away the pattern for the URL, so we can access this component at run-time.
@page "/myroute"
As previously mentioned, the @page directive can also be used to specify the pattern for accepting parameters in the route.
@page "/counter/{MyParameter}"
@code {
[Parameter]
public string MyParameter { get; set; }
}
Because of the use of the [Parameter] attribute here, Blazor will automatically initialize the property MyParameter with the corresponding value from the route parameters. For instance, if we run our app and navigate to.
"/counter/myValue"
The value of MyParameter will be the string “myValue” when the component has finished loading. Here is a way we can visualize a simple Parameter.

A Quick Primer On Attributes in C#
If you’re unfamiliar with attributes in C#, it will be hard to understand how this [Parameter] functionality actually works in Blazor. In a previous section, I stated “Blazor will automatically initialize the property …” and this is true. Attributes do not have inherent behaviors in C# or .NET, and they generally do not influence any external data on their own. Attributes are just metadata on a property, and this metadata needs to be consumed by some other code.
But how are attributes implemented?
Attributes are simple classes that follow a particular naming convention, ClassNameAttribute —signaling to the C# compiler that this class will be used as an annotation in another class.
Say, for example, I want a custom attribute called [ValidationApplied]. I would create a new class called ValidationAppliedAttribute which will inherit from Attribute.
class ValidationAppliedAttribute : Attribute
{
}
Now this class can be used as an attribute in another class.
class SomeClass
{
[ValidationApplied] // custom attribute
public string MyProperty { get; set; }
}
What’s interesting about this is that on its own, the attribute doesn’t do anything. It is merely metadata on a property and doesn’t encapsulate any behaviors. Attributes can have their own properties, too.
class ValidationAppliedAttribute : Attribute
{
public string MyProperty { get; set; }
}
And now the following syntax is possible.
[ValidationApplied(MyProperty = "some value")]
public string SomeProperty { get; set; }
Given the fact that attributes don’t inherently encapsulate any behaviors, we need to write the behaviors ourselves. This is done using reflection elsewhere in the codebase.
var attributes = Attribute.GetCustomAttributes(myObject.GetType()).Dump();
foreach (var attribute in attributes)
{
if (attribute is ValidationAppliedAttribute)
{
// property has [ValidationApplied]. test validation on property
}
}
We can be certain this type of reflection is being performed when we use the [Parameter] annotation in a Blazor Component. Using reflection does carry some performance overhead, but over time this overhead has gotten smaller and smaller. I'm only mentioning all of this because it’s a good idea, in my opinion, to be aware of what our code is actually doing when we use these very abstract frameworks—especially when we’re paying for compute time on the Cloud. But, we also need not jump to any conclusions about performance just by looking at an implementation detail alone. For instance, many frameworks that do this type of reflection may implement caching. They’ll do the reflection work once and cache the results. After all, attributes are applied to Types, not Instances. Therefore, the work done using reflection only really needs to be done once per type.



Hassan GulzarPosted Oct 3, 2022, 6:51 PM
Under the 'Optional Parameters' heading, your code sample tests a non-existent variable 'StartValue' for nullability. It should be 'InitialValue' and not 'StartValue'. Great article, by the way.