C#: 单例模式的接口实现行为不同

huangapple go评论79阅读模式
英文:

C#: Interface Implementation behaves differently for Singleton

问题

以下是翻译的内容:

1st statement:

Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();

第一句:

Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();

2nd statement:

public Guid Id => Guid.NewGuid();

第二句:

public Guid Id => Guid.NewGuid();

IReportServiceLifetime.cs:

public interface IReportServiceLifetime
{
    Guid Id { get; }
    ServiceLifetime Lifetime { get; }
}

IReportServiceLifetime.cs:

public interface IReportServiceLifetime
{
    Guid Id { get; }
    ServiceLifetime Lifetime { get; }
}

IExampleSingletonService.cs:

public interface IExampleSingletonService : IReportServiceLifetime
{
    ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Singleton;
}

IExampleSingletonService.cs:

public interface IExampleSingletonService : IReportServiceLifetime
{
    ServiceLifetime IReportServiceLifetime.Lifetime => ServiceLifetime.Singleton;
}

ExampleSingletonService.cs:

internal sealed class ExampleSingletonService : IExampleSingletonService
{
    Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
   // public Guid Id => Guid.NewGuid();
}

ExampleSingletonService.cs:

internal sealed class ExampleSingletonService : IExampleSingletonService
{
    Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
   // public Guid Id => Guid.NewGuid();
}

ServiceLifetimeReporter.cs:

internal sealed class ServiceLifetimeReporter
{

    private readonly IExampleSingletonService _singletonService;

    public ServiceLifetimeReporter(IExampleSingletonService singletonService) =>
        (_singletonService) =
            (singletonService);


    public void ReportServiceLifetimeDetails(string lifetimeDetails)
    {
        Console.WriteLine(lifetimeDetails);

        LogService(_singletonService, "Always the same");
    }

    private static void LogService<T>(T service, string message)
        where T : IReportServiceLifetime =>
        Console.WriteLine(
            $"    {typeof(T).Name}: {service.Id} ({message})");
}

ServiceLifetimeReporter.cs:

internal sealed class ServiceLifetimeReporter
{

    private readonly IExampleSingletonService _singletonService;

    public ServiceLifetimeReporter(IExampleSingletonService singletonService) =>
        (_singletonService) =
            (singletonService);


    public void ReportServiceLifetimeDetails(string lifetimeDetails)
    {
        Console.WriteLine(lifetimeDetails);

        LogService(_singletonService, "Always the same");
    }

    private static void LogService<T>(T service, string message)
        where T : IReportServiceLifetime =>
        Console.WriteLine(
            $"    {typeof(T).Name}: {service.Id} ({message})");
}

Program.cs:

using IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddSingleton<IExampleSingletonService, ExampleSingletonService>();
        services.AddTransient<ServiceLifetimeReporter>();
    })
    .Build();



ServiceLifetime(host.Services, "Lifetime 1");
ServiceLifetime(host.Services, "Lifetime 2");

await host.RunAsync();

static void ServiceLifetime(IServiceProvider hostProvider, string lifetime)
{
    using IServiceScope serviceScope = hostProvider.CreateScope();
    IServiceProvider provider = serviceScope.ServiceProvider;
    ServiceLifetimeReporter logger = provider.GetRequiredService<ServiceLifetimeReporter>();
    logger.ReportServiceLifetimeDetails(
        $"{lifetime}: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()");

    Console.WriteLine("...");

    logger = provider.GetRequiredService<ServiceLifetimeReporter>();
    logger.ReportServiceLifetimeDetails(
        $"{lifetime}: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()");

    Console.WriteLine();
}

Program.cs:

using IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddSingleton<IExampleSingletonService, ExampleSingletonService>();
        services.AddTransient<ServiceLifetimeReporter>();
    })
    .Build();



ServiceLifetime(host.Services, "Lifetime 1");
ServiceLifetime(host.Services, "Lifetime 2");

await host.RunAsync();

static void ServiceLifetime(IServiceProvider hostProvider, string lifetime)
{
    using IServiceScope serviceScope = hostProvider.CreateScope();
    IServiceProvider provider = serviceScope.ServiceProvider;
    ServiceLifetimeReporter logger = provider.GetRequiredService<ServiceLifetimeReporter>();
    logger.ReportServiceLifetimeDetails(
        $"{lifetime}: Call 1 to provider.GetRequiredService<ServiceLifetimeReporter>()");

    Console.WriteLine("...");

    logger = provider.GetRequiredService<ServiceLifetimeReporter>();
    logger.ReportServiceLifetimeDetails(
        $"{lifetime}: Call 2 to provider.GetRequiredService<ServiceLifetimeReporter>()");

    Console.WriteLine();
}
英文:

What is the difference between below two implementations?

       Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid(); //1st statement
       // public Guid Id =&gt; Guid.NewGuid(); //2nd statement

The below lines of code behaving differently for Singleton

For the above case (2nd statement is commented out) the code works correctly. One can find out by looking at same Guid for all the requests.

C#: 单例模式的接口实现行为不同

But if one runs by making following changes. The Guid is different for all the requests.

       //Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid(); //1st statement
       public Guid Id =&gt; Guid.NewGuid(); //2nd statement

C#: 单例模式的接口实现行为不同

IReportServiceLifetime.cs

    public interface IReportServiceLifetime
    {
        Guid Id { get; }
        ServiceLifetime Lifetime { get; }
    }

IExampleSingletonService.cs

    public interface IExampleSingletonService : IReportServiceLifetime
    {
        ServiceLifetime IReportServiceLifetime.Lifetime =&gt; ServiceLifetime.Singleton;
    }

ExampleSingletonService.cs

    internal sealed class ExampleSingletonService : IExampleSingletonService
    {
        Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
       // public Guid Id =&gt; Guid.NewGuid();
    }

ServiceLifetimeReporter.cs

 internal sealed class ServiceLifetimeReporter
    {

        private readonly IExampleSingletonService _singletonService;

        public ServiceLifetimeReporter(IExampleSingletonService singletonService) =&gt;
        (_singletonService) =
            (singletonService);


        public void ReportServiceLifetimeDetails(string lifetimeDetails)
        {
            Console.WriteLine(lifetimeDetails);

            LogService(_singletonService, &quot;Always the same&quot;);
        }

        private static void LogService&lt;T&gt;(T service, string message)
            where T : IReportServiceLifetime =&gt;
            Console.WriteLine(
                $&quot;    {typeof(T).Name}: {service.Id} ({message})&quot;);
    }

Program.cs

using IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =&gt;
    {
        services.AddSingleton&lt;IExampleSingletonService, ExampleSingletonService&gt;();
        services.AddTransient&lt;ServiceLifetimeReporter&gt;();
    })
    .Build();



ServiceLifetime(host.Services, &quot;Lifetime 1&quot;);
ServiceLifetime(host.Services, &quot;Lifetime 2&quot;);

await host.RunAsync();

static void ServiceLifetime(IServiceProvider hostProvider, string lifetime)
{
    using IServiceScope serviceScope = hostProvider.CreateScope();
    IServiceProvider provider = serviceScope.ServiceProvider;
    ServiceLifetimeReporter logger = provider.GetRequiredService&lt;ServiceLifetimeReporter&gt;();
    logger.ReportServiceLifetimeDetails(
        $&quot;{lifetime}: Call 1 to provider.GetRequiredService&lt;ServiceLifetimeReporter&gt;()&quot;);

    Console.WriteLine(&quot;...&quot;);

    logger = provider.GetRequiredService&lt;ServiceLifetimeReporter&gt;();
    logger.ReportServiceLifetimeDetails(
        $&quot;{lifetime}: Call 2 to provider.GetRequiredService&lt;ServiceLifetimeReporter&gt;()&quot;);

    Console.WriteLine();
}

答案1

得分: 2

你所写的是一种通常被称为“语法糖”的属性简化语法。C#编译器将其扩展为以下等效代码:

private readonly Guid _Id1_BackingField = Guid.NewGuid();
public Guid Id1
{
    get
    {
        return _Id1_BackingField;
    }
}

public Guid Id2
{
    get
    {
        return Guid.NewGuid();
    }
}

但是,这个后备字段是隐藏的。

Id 的创建表达式被转换为只读后备字段的字段初始化程序。

而对于 Id2,创建表达式用于返回语句中,显然每次读取属性时都会调用。

您可以在 SharpLab.io 上进行测试。C#编译器用来隐藏后备字段的技巧是使用在C#中无效的标识符(&lt;Id1&gt;k__BackingField)。

请注意,C#编译器生成IL代码。此标识符在IL中是有效的(您可以通过右侧面板的下拉框切换到IL视图)。

英文:

What you wrote is a simplified syntax for properties that is often called "syntactic sugar". The C# compiler expands this:

public Guid Id1 { get; } = Guid.NewGuid();

public Guid Id2 =&gt; Guid.NewGuid();

... to a code equivalent to:

private readonly Guid _Id1_BackingField = Guid.NewGuid();
public Guid Id1
{
    get
    {
        return _Id1_BackingField;
    }
}

public Guid Id2
{
    get
    {
        return Guid.NewGuid();
    }
}

The backing field is however hidden.

The creation expression of Id is converted to a field initializer of a read-only backing field.

Whereas for Id2 the creation expression is used in a return-statement which is obviously called every time you are reading the property.

You can test this on SharpLab.io. The trick the C# compiler uses to hide the backing field is to use an identifier which is invalid in C# (&lt;Id1&gt;k__BackingField).

Note that the C# compiler generates IL code. This identifier is valid in IL. (You can switch to IL view through the drop-down-box on the right panel.)

huangapple
  • 本文由 发表于 2023年5月29日 20:03:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/76357205.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定