Less Boilerplate More Clarity

We eliminate repetitive code from your C# projects
so you can get back to the bright side of programming.

More than 10% of all Fortune 500 companies have already chosen PostSharp.

Your code. Cleaner.

Without PostSharp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
public class Address : INotifyPropertyChanged
{
    private string _line1;
    private string _line2;
    private string _city;
    private string _remark;

    public string Line1
    {
        get => _line1;
        set
        {
            _line1 = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(FullAddress));
        }
    }

    public string Line2
    {
        get => _line2;
        set
        {
            _line2 = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(FullAddress));
        }
    }

    public string City
    {
        get => _city;
        set
        {
            _city = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(FullAddress));
        }
    }

    public string Remark
    {
        get => _remark;
        set
        {
            _remark = value;
            OnPropertyChanged();
        }
    }

    public string FullAddress => string.Join(Environment.NewLine, Line1, Line2, City);

    public event PropertyChangedEventHandler PropertyChanged;

    // TODO: Weak event.
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class Person : INotifyPropertyChanged
{
    private string _firstName;
    private string _lastName;
    private Address _address;

    public string FirstName
    {
        get => _firstName;
        set
        {
            _firstName = value;
            OnPropertyChanged();
        }
    }

    public string LastName
    {
        get => _lastName;
        set
        {
            _lastName = value;
            OnPropertyChanged();
        }
    }

    public Address Address
    {
        get => _address;
        set
        {
            _address = value;
            OnPropertyChanged();
        }

    }


    public event PropertyChangedEventHandler PropertyChanged;

    // TODO: Weak event.
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

}

public class PersonViewModel : INotifyPropertyChanged
{
    private Person _person;
    private bool _includeAddressInDetails;

    // TODO: Weak reference (if weak events are implemented).
    private Address _observedAddress;

    public Person Person
    {
        get => _person;
        set
        {

            if (_person != null)
            {
                _person.PropertyChanged -= OnPersonPropertyChanged;
                if (_observedAddress != null)
                {
                    _observedAddress.PropertyChanged -= OnAddressPropertyChanged;
                    _observedAddress = null;
                }
            }
            _person = value;

            if (value != null)
            {
                value.PropertyChanged += OnPersonPropertyChanged;
                _observedAddress = value.Address;
                if (_observedAddress != null)
                {
                    _observedAddress.PropertyChanged += OnAddressPropertyChanged;
                }
            }
            OnPropertyChanged();
            OnPropertyChanged(nameof(FullDetails));


        }
    }


    private void OnAddressPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == nameof(Address.FullAddress))
        {
            OnPropertyChanged(nameof(FullDetails));
        }
    }




    private void OnPersonPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case nameof(Person.FirstName):
            case nameof(Person.LastName):
                OnPropertyChanged(nameof(FullDetails));
                break;

            case nameof(Person.Address):
                if (_observedAddress != null)
                {
                    _observedAddress.PropertyChanged -= OnAddressPropertyChanged;
                    _observedAddress = null;
                }

                _observedAddress = _person.Address;
                if (_observedAddress != null)
                {
                    _observedAddress.PropertyChanged += OnAddressPropertyChanged;
                }

                break;
        }
    }

    public bool IncludeAddressInDetails
    {
        get => _includeAddressInDetails;
        set
        {
            _includeAddressInDetails = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(FullDetails));
        }
    }

    public string FullDetails
    {
        get
        {
            // We don't handle the nulls for simplicity of the argument.
            if (IncludeAddressInDetails)
            {
                return string.Join(this.Person.FirstName + " " + this.Person.LastName,
                    this.Person.Address.FullAddress);
            }
            else
            {
                return this.Person.FirstName + " " + this.Person.LastName;
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    // TODO: Weak events.
    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
With PostSharp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
[NotifyPropertyChanged]
public class Address
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string City { get; set; }
    public string Remark { get; set; }
    public string FullAddress => string.Join(Environment.NewLine, Line1, Line2, City);
}

[NotifyPropertyChanged]
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Address Address { get; set; }
}

[NotifyPropertyChanged]
public class PersonViewModel
{
    public Person Person { get; set; }
    public bool IncludeAddressInDetails { get; set; }

    public string FullDetails
    {
        get
        {
            // We don't handle the nulls for simplicity of the argument.
            if (IncludeAddressInDetails)
            {
                return string.Join(this.Person.FirstName + " " + this.Person.LastName,
                    this.Person.Address.FullAddress);
            }
            else
            {
                return this.Person.FirstName + " " + this.Person.LastName;
            }
        }
    }
}
Without PostSharp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public partial class MainWindow : Window
{
    private MainViewModel _mainViewModel = new MainViewModel();

    public MainWindow()
    {
        InitializeComponent();
        this.OpenFile = new OpenFileCommand(_mainViewModel);
        this.CloseFile = new CloseFileCommand(_mainViewModel);
    }

    public ICommand OpenFile { get; }

    public ICommand CloseFile { get; }

    class OpenFileCommand : ICommand
    {
        private MainViewModel _mainViewModel;

        public OpenFileCommand(MainViewModel mainViewModel)
        {
            _mainViewModel = mainViewModel;
        }

        public bool CanExecute(object parameter) => true;

        public void Execute(object parameter) => _mainViewModel.OpenFile();

        public event EventHandler CanExecuteChanged;
    }

    class CloseFileCommand : ICommand
    {
        private MainViewModel _mainViewModel;

        public CloseFileCommand(MainViewModel mainViewModel)
        {
            _mainViewModel = mainViewModel;

            _mainViewModel.PropertyChanged += delegate (object o, PropertyChangedEventArgs args)
                {
                    if (args.PropertyName == nameof(MainViewModel.IsFileOpen))
                    {
                        this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
                    }
                };
        }

        public bool CanExecute(object parameter) => _mainViewModel.IsFileOpen;

        public void Execute(object parameter) => _mainViewModel.CloseFile();

        public event EventHandler CanExecuteChanged;
    }
}

public partial class FancyTextBlock : UserControl
{
    public static DependencyProperty TextProperty = DependencyProperty.Register(
      "Text", typeof(string), typeof(FancyTextBlock), null,
      value => !string.IsNullOrEmpty(value as string));

    public string Text
    {
        get => (string)this.GetValue(TextProperty);
        set => this.SetValue(TextProperty, value);
    }
}
With PostSharp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public partial class MainWindow : Window
{
    private MainViewModel _mainViewModel = new MainViewModel();

    public MainWindow()
    {
        InitializeComponent();
    }

    [Command]
    public ICommand OpenFile { get; private set; }

    private void ExecuteOpenFile() => _mainViewModel.OpenFile();


    [Command]
    public ICommand CloseFile { get; private set; }

    private void ExecuteCloseFile() => _mainViewModel.CloseFile();
    public bool CanExecuteCloseFile => _mainViewModel.IsFileOpen;
}

public partial class FancyTextBlock : UserControl
{
    [DependencyProperty, Required]
    public string Text { get; set; }
}
Without PostSharp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class Icecream : IFreezable
{
    private double _temperature;
    public FreezableList<Ingredient> Ingredients { get; } = new FreezableList<Ingredient>();

    public double Temperature
    {
        get => _temperature;
        set
        {
            if (this.IsFrozen) throw new ObjectReadOnlyException();
            _temperature = value;
        }

        // Hello, world.
    }

    public void Freeze()
    {
        this.IsFrozen = true;
        this.Ingredients.Freeze();
    }

    public bool IsFrozen { get; private set; }
}


public class Ingredient : IFreezable
{
    private string _name;
    private double _quantity;

    public string Name
    {
        get => _name;

        set
        {
            if (this.IsFrozen) throw new ObjectReadOnlyException();

            _name = value;
        }
    }

    public double Quantity
    {
        get => _quantity;

        set
        {
            if (this.IsFrozen) throw new ObjectReadOnlyException();

            _quantity = value;
        }
    }

    public void Freeze()
    {
        this.IsFrozen = true;
    }

    public bool IsFrozen { get; private set; }
}
With PostSharp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Freezable]
public class Icecream
{
    public FreezableList<Ingredient> Ingredients { get; } = new FreezableList<Ingredient>();

    public double Temperature { get; set; }
}

[Freezable]
public class Ingredient
{
    public string Name { get; set; }

    public double Quantity { get; set; }
}
Without PostSharp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class UserNameService
{
    private Logger logger;
    private Db db;

    // It's just one method, but you should do this for thousands.
    public string GetUserName(int userId)
    {
        var methodLogger = logger.Start("UserNameService.GetUserName({userId})");

        try
        {

            string userName = db.Context.Users.Single(u => u.Id == userId).Name;
            methodLogger.Success(userName);

            return userName;
        }
        catch (Exception e)
        {
            methodLogger.Exception(e);
            throw;
        }
    }
}
With PostSharp
1
2
3
4
5
6
7
8
9
10
11
public class UserNameService
{
    private Db db;

    // Logging is generally configured in an xml config file.
    public string GetUserName(int userId)
    {
        return db.Context.Users.Single(u => u.Id == userId).Name;
    }
}
Without PostSharp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public interface IBananaFactory
{
    Banana GetBanana(string kind, double length, double diameter);
}

public class BananaFactory : IBananaFactory
{
    public Banana GetBanana(string kind, double length, double diameter)
    {
        if (string.IsNullOrEmpty(kind)) throw new ArgumentNullException(nameof(kind));
        if (length < 0) throw new ArgumentOutOfRangeException(nameof(length));
        // Yep copy-pase error again.
        if (length < 0 || diameter > 5) throw new ArgumentOutOfRangeException(nameof(length));

        return new Banana(kind, length, diameter);
    }
}

public class Banana
{
    private string _kind;
    private double _length;
    private double _diameter;

    public Banana(string kind, double length, double diameter)
    {
        Kind = kind;
        Length = length;
        Diameter = diameter;
    }

    public string Kind
    {
        get => _kind;
        set
        {
            if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value));
            _kind = value;
        }
    }

    public double Length
    {
        get => _length;
        set
        {
            if (value < 0) throw new ArgumentOutOfRangeException(nameof(value));
            _length = value;
        }
    }

    public double Diameter
    {
        get => _diameter;
        set
        {
            if (value < 0 || value > 5) throw new ArgumentOutOfRangeException(nameof(value));
            _diameter = value;
        }
    }
}
With PostSharp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public interface IBananaFactory
{
    Banana GetBanana([Required] string kind, [Positive] double length, [Range(0, 5)] double diameter);
}

public class BananaFactory : IBananaFactory
{
    public Banana GetBanana(string kind, double length, double diameter)
    {
        return new Banana(kind, length, diameter);
    }
}

public class Banana
{
    public Banana(string kind, double length, double diameter)
    {
        Kind = kind;
        Length = length;
        Diameter = diameter;
    }

    [Required]
    public string Kind { get; set; }

    [Positive]
    public double Length { get; set; }

    [Range(0, 5)]
    public double Diameter { get; set; }

}
n class="codePunctuation">}
INPC
XAML
THREADING
LOGGING
CONTRACTS

What does boilerplate cost you?

Writing Boilerplate Is a Total Waste of Your Time

Boilerplate Makes Your Application Unreliable

Boilerplate Makes Your Application Unmaintainable

How does it work?

1. Add the PostSharp package to your project and annotate code with [CustomAttributes].

2. The C# or VB compiler builds your code into binaries.

3. PostSharp analyzes the binary and injects the implementation of the aspects.

Your app, production-ready

At run time, both your business logic and the aspects are seamlessly executed. Your source code remains crystal clear.

Make a better use of your time.

Move 10–50% of your implementation workloads from the team to the compiler.

Focus on meaningful and inventive tasks instead.

Professionally engineered

Developer–friendly License

  • Usage-based and user-based licensing.
  • Includes 1 year of support and updates.
  • Unlimited number of build servers.

Professionally Supported

  • Backed by a stable company.
  • Excellent documentation.
  • New releases every 3 weeks.

Platform Support

  • .NET Framework and .NET Core.
  • Visual Studio 2015, 2017, 2019.
  • Build servers, Azure DevOps, Docker.

Success stories

Siemens Audiology
ATS Global
Thales
Gamesys
CognitiveX
siemens-case-study

Siemens Audiology

Siemens Audiology saved 15% development time plus improved code readability by removing unwanted boilerplate code.

When the team at Siemens Audiology was tasked with building a new WPF implementation for two of its leading hearing system software applications, a big challenge was to find a way to save coding time for developers implementing ViewModels while increasing code readability.

ats-global-casestudy

ATS Global

ATS Global saves 16% lines of code.

When the team at ATS Global needed to build a complex shop floor simulation, they faced the challenge of potential multithreading issues and the complexity of writing synchronization code.

The team turned to PostSharp and was able to write thread-safe code without training all team members in advanced multithreading and delivered required features with 16% fewer lines of code.

thales-casestudy

Thales Information Systems

Thales is more focused on business logic.

When the team at Thales Information Systems was tasked with a big refactoring on a project, one of the challenges was to simplify architecture by reducing boilerplate code. The architect chose PostSharp to implement custom aspects to handle logging, performance counters and INotifyPropertyChanged and keep junior team members better focused on business logic.

gamesys-casestudy

Gamesys

Gamesys improves productivity with PostSharp.

Gamesys serves around one million daily active users across its social games. Their backend services handle more than 250 million requests per day. Despite its massive scale, this distinctive service is being maintained by a remarkably small development team of just seven super-productive individuals.

cognitivex-casestudy

Cognitive X Solutions

PostSharp took care of 95% of CognitiveX's INPC code.

When Cognitive X was looking for ways to deliver value to their clients while differentiating themselves from the competition, the team chose PostSharp as one of the core pieces of their strategy. By decreasing the actual lines of code they were writing, as well as helping them enforce best practices, PostSharp allowed them to produce new features for their clients faster, at lower cost, and with fewer errors.

Featured from the blog

Over 50,000 satisfied users

"PostSharp is a pretty amazing piece of software."

Stay DRY with aspect oriented programming by injecting repetitive code directly into your application. Abstractions like AOP are the whole point of what the computer is supposed to do for us, work that’s not fun, like logging and transactions. So why not hide that?

Scott Hanselman
Principal Program Manager, Microsoft

"PostSharp should be part of every C# developer's toolset."

PostSharp is amazing! If you are not using it, you should! The amount of code that can be reduced in logging alone is worth the price! I believe that PostSharp should be part of EVERY C# developer's toolset.

Eric Ziko
System Programmer, Wells Fargo

"Working declaratively is a dream!"

PostSharp is the best way to implement cross-cutting functionalities in a very elegant and efficient way. And when you want to enforce some design rules in your code, working declaratively is a dream!

Pierre-Emmanuel Dautreppe
.NET Architect, Thales Information Systems

"PostSharp simplicity is simply amazing."

PostSharp rocks! And I mean that as someone who is doing AOP for a long while, and has implemented some not insignificant parts of Castle.DynamicProxy. Leaving aside the amount of power that it gives you, PostSharp simplicity is simply amazing, wow!

Oren Eini
Author, RhinoMocks

"PostSharp is very easy to use."

Releasing developers from writing boilerplate or infrastructure code helps my team to complete features faster. We definitely save coding time with PostSharp. We’re relieved from writing INotifyPropertyChanged and creating ICommand properties needed for data binding.

Bernd Hengelein
Software Architect, Siemens Audiology

Subscribe to our newsletter

Stay up to date on all the latest releases, news and discounts.
Follow us on social media: