Open sandboxFocusImprove this doc

LAMA0550: The Initialize method of a non-sealed class must be public and virtual

Severity Error
Reported by Metalama.Framework
Message The aspect '<AspectType>' cannot add an initializer to the 'Initialize' method of type '<TargetType>' because the method is not both 'public' and 'virtual' (or 'override'). Since '<TargetType>' is not sealed, its 'Initialize' method must be 'public virtual' so that derived types can extend the initialization. Make the method 'public virtual', or make the type 'sealed'.

Cause

When an aspect calls AddInitializer with AfterObjectInitializer, Metalama adds the aspect's code to the Initialize( InitializationContext ) method of the target type, which implements IInitializable. Metalama calls this method after the object initializer. If the target type doesn't have an Initialize method, Metalama introduces one, and makes it public virtual when the type isn't sealed. If the type already has a hand-written Initialize( InitializationContext ) method, Metalama adds the aspect's code to this method.

In a class that isn't sealed, a derived class must be able to override Initialize and call base.Initialize(...), so that the initialization logic of all levels of the hierarchy runs. Metalama reports LAMA0550 when the hand-written Initialize method of a non-sealed class isn't public, or is neither virtual nor override.

In the following example, the Document class isn't sealed, and its Initialize method isn't virtual.

1using Metalama.Framework.Advising;
2using Metalama.Framework.Aspects;
3using Metalama.Framework.Code;
4using System;
5
6namespace Doc.LAMA0550.Error;
7
8public class LogInitializedAttribute : TypeAspect
9{
10    public override void BuildAspect( IAspectBuilder<INamedType> builder )
11    {
12        // Adds the template to the Initialize method, which runs after the object initializer.
13        builder.AddInitializer( nameof(this.LogInitialization), InitializerKind.AfterObjectInitializer );
14    }
15
16    [Template]
17    private void LogInitialization()
18    {
19        Console.WriteLine( $"{meta.Target.Type.Name} initialized." );
20    }
21}
22
1using Metalama.Framework.RunTime.Initialization;
2
3namespace Doc.LAMA0550.Error;
4
5[LogInitialized]
Error LAMA0550: The aspect 'LogInitialized' cannot add an initializer to the 'Initialize' method of type 'Document' because the method is not both 'public' and 'virtual' (or 'override'). Since 'Document' is not sealed, its 'Initialize' method must be 'public virtual' so that derived types can extend the initialization. Make the method 'public virtual', or make the type 'sealed'.

6public partial class Document : IInitializable
7{
8    public string Title { get; init; } = "Untitled";
9
10    // Error: the class isn't sealed, so Initialize must be public virtual.
11    public void Initialize( InitializationContext context = default ) { }
12}
13

Resolution

Do one of the following:

  • Make the Initialize method public virtual, or public override if it overrides a base implementation.
  • If the class isn't meant to be derived from, mark it as sealed.
  • Remove the hand-written Initialize method and let Metalama introduce it.

In the following example, the Initialize method is made virtual.

1using Metalama.Framework.Advising;
2using Metalama.Framework.Aspects;
3using Metalama.Framework.Code;
4using System;
5
6namespace Doc.LAMA0550.Fixed;
7
8public class LogInitializedAttribute : TypeAspect
9{
10    public override void BuildAspect( IAspectBuilder<INamedType> builder )
11    {
12        // Adds the template to the Initialize method, which runs after the object initializer.
13        builder.AddInitializer( nameof(this.LogInitialization), InitializerKind.AfterObjectInitializer );
14    }
15
16    [Template]
17    private void LogInitialization()
18    {
19        Console.WriteLine( $"{meta.Target.Type.Name} initialized." );
20    }
21}
22
Source Code
1using Metalama.Framework.RunTime.Initialization;
2

3namespace Doc.LAMA0550.Fixed;
4
5[LogInitialized]
6public partial class Document : IInitializable
7{
8    public string Title { get; init; } = "Untitled";
9
10    // Fixed: Initialize is virtual, so derived types can extend the initialization.
11    public virtual void Initialize( InitializationContext context = default ) { }
12}
13
Transformed Code
1using System;
2using Metalama.Framework.RunTime.Initialization;
3
4namespace Doc.LAMA0550.Fixed;
5
6[LogInitialized]
7public partial class Document : IInitializable
8{
9    public string Title { get; init; } = "Untitled";
10
11    // Fixed: Initialize is virtual, so derived types can extend the initialization.
12    public virtual void Initialize(InitializationContext context = default)
13    {
14        Console.WriteLine("Document initialized.");
15    }
16}
17

See also