简体   繁体   中英

Correct static field/property getonly/readonly using

I have an abstract base class Bank and its successors BankOfAmerica: Bank and JPMorganChase: Bank . I want to make one of them default in case of creation BankAccount instance without indication exact Bank in BankAccount constructor, so I created a static class GeneralBankConfig with DefaultBank field (or property? or method?).

Which of the following ways is better and why?

  1. public static Bank DefaultBank { get; } = new BankOfAmerica();
  2. public static Bank DefaultBank { get { return new BankOfAmerica(); } }
  3. public static readonly Bank DefaultBank = new BankOfAmerica();
  4. public static Bank GetDefaultBank() { return new BankOfAmerica(); }

Is it a good way to store configurations?

To understand the differences, it helps to look at the compiled C#, with unique names so that all four implementations can live side-by-side.

public static class GeneralBankConfig
{
    public static Bank DefaultBank1 { get; } = new BankOfAmerica();
    public static Bank DefaultBank2 { get { return new BankOfAmerica(); } }
    public static readonly Bank DefaultBank3 = new BankOfAmerica();
    public static Bank GetDefaultBank4() { return new BankOfAmerica(); }
}

public abstract class Bank { }
public class BankOfAmerica : Bank { }

This compiles to the following:

public static class GeneralBankConfig
{
    [CompilerGenerated]
    private static readonly Bank <DefaultBank1>k__BackingField = new BankOfAmerica();

    public static readonly Bank DefaultBank3 = new BankOfAmerica();

    public static Bank DefaultBank1
    {
        [CompilerGenerated]
        get
        {
            return <DefaultBank1>k__BackingField;
        }
    }

    public static Bank DefaultBank2
    {
        get
        {
            return new BankOfAmerica();
        }
    }

    public static Bank GetDefaultBank4()
    {
        return new BankOfAmerica();
    }
}
public abstract class Bank
{
}
public class BankOfAmerica : Bank
{
}

Option 1 compiles to a read only public property with a compile-generated backing field. This is typically the most common way to express public apis in instance classes since they can be exposed via interfaces. However, for static properties, that becomes more of a preference, although there may be a slight additional cost for invoking the get_DefaultBank1() method under the hood as opposed to referencing the static field directly.

Option 2 compiles to a public readonly property that returns a new BankOfAmerica instance every time it is called. This is most likely the opposite of what you want since you are no longer using a "cached" instance.

Option 3 compiles to a public readonly field. See the notes on option 1.

Option 4 compiles to a public static method that returns a new instance of BankOfAmerica every time it is called. This is the worst option since not only does it create a new object instance, but there are costs to calling the method that do not exist when referencing a field.

You can see the IL for all four invocations here:

IL_0001 call    GeneralBankConfig.get_DefaultBank1 ()
IL_0006 stloc.0    // defaultBank1
IL_0007 call    GeneralBankConfig.get_DefaultBank2 ()
IL_000C stloc.1    // defaultBank2
IL_000D ldsfld  GeneralBankConfig.DefaultBank3
IL_0012 stloc.2    // defaultBank3
IL_0013 call    GeneralBankConfig.GetDefaultBank4 ()
IL_0018 stloc.3    // defaultBank4

In short, choose option 1 or 3, depending on whether you'd prefer to expose a field or property.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM