只读字段的对象初始化语法 [HttpsRequestMessage.Header]

3

在搜索如何处理HTTP请求头时,我看到很多示例使用以下结构来初始化HttpRequestMessage:

var request = new HttpRequestMessage()
{
    Headers =
    {
        { HttpRequestHeader.Authorization.ToString(), Token },
        { HttpRequestHeader.ContentType.ToString(), "multipart/form-data" }
    },
    Method = HttpMethod.Post,
    RequestUri = new Uri(endpointUrl),
    Content = content
};

这似乎很好地运作了,编译器没有抱怨,甚至没有注册警告,但我对Headers字段的初始化非常困惑。
源代码中,Headers字段被定义为:
public HttpRequestHeaders Headers
{
    get
    {
        if (headers == null)
        {
            headers = new HttpRequestHeaders();
        }
        return headers;
    }
}

我在想,如何初始化只有get函数的字段?即使它以某种方式初始化了底层的私有HttpRequestHeaders headers(尽管我相当确定它并不是这样工作的),我从未见过使用C#中的“Field = {{...},{...}}”类型初始化的情况。 这类初始化方式类似于字典初始化器,但缺少“new HttpRequestHeaders”,因此不能使用该方法。这是我所见过的唯一一次使用此类型初始化,并且在文档或SO上找不到任何参考资料。

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#object-initializers-with-collection-read-only-property-initialization - user19858830
@MySkullCaveIsADarkPlace 我已经多次阅读了那份文档,但是里面没有解释“Field = { { ... }, { ... } }”类型的初始化方式,也没有回答我们如何初始化getter函数的问题。 - Mario Š.
你的意思是,你对{ HttpRequestHeader.Authorization.ToString(), Token }{ HttpRequestHeader.ContentType.ToString(), "multipart/form-data" }这两部分感到困惑,而不是特别关注在只读属性上使用集合初始化器语法?(如果是这样,请编辑你的问题以澄清……) - user19858830
哦,没错。我确实搜索了好几次,但一直在寻找“{{...},{...}}”的模式,但由于他们使用对象列表(“new Cat(...)”),所以这种模式并没有被使用。我的错。谢谢! - Mario Š.
2个回答

0
如何初始化一个只有get函数的字段?
如果一个属性没有setter,这并不意味着你不能初始化它。这意味着在对象初始化之后你不能改变它的值。
因此,以下代码将生成编译时错误:
var request = new HttpRequestMessage()
{
    Method = HttpMethod.Post,
    RequestUri = new Uri(endpointUrl),
    Content = content
};
//This is NOT allowed
request.Headers =
{
    { HttpRequestHeader.Authorization.ToString(), Token },
    { HttpRequestHeader.ContentType.ToString(), "multipart/form-data" }
},

只有 getter 的属性与 readonly 字段的工作方式相同。
它们可以在声明时初始化。

public class Test
{
   public readonly int X = 1;
   public int Y { get; } = 1;
}

或者在任何构造函数内部

public class Test
{
    public readonly int X;
    public int Y { get; }
    
    public Test()
    {
        X = 1;
        Y = 1;
    }
    
    public Test(int x, int y)
        => (X, Y) = (x, y);
}

如果只读字段/只读属性的数据类型不是值类型(class/record),则可以使用对象初始化器。

public class Test
{
    public readonly TestData X;
    public TestData Y { get; }
}

public class TestData
{
    public int Z { get; set; }
}

var t1 = new Test { Y = { Z = 1 }, X = { Z = 1 } };
//OR
Test t2 = new () { Y = { Z = 1 }, X = { Z = 1 } };

如果 TestData 是一个 structrecord struct,那么代码将无法编译。
如果您想要一个属性在初始化后不能被设置且其数据类型为值类型,那么您需要使用init
public class Test
{
    public int Y { get; init; };
}

var t = new Test { Y = 1 };
//This is NOT allowed
t.Y = 2;

所以,你可以在对象初始化器中设置HttpRequestMessageHeader属性,因为Header的数据类型是HttpRequestHeaders,它是一个引用类型。

0

HttpRequestHeader实现了System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<string,System.Collections.Generic.IEnumerable<string>>>

这意味着您可以向其中添加元素,此处为KeyValuePair元素。您不是在初始化Headers属性,而是将KeyValuePairs添加到其中。

请参见https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#object-initializers-with-collection-read-only-property-initialization


1
实际上,使问题中的语法工作所需的只是相应类型实现非泛型 IEnumerable 接口(泛型 IEnumerable<T> 不是必需的)并提供 public Add(string,string) 方法 - HttpRequestHeaders 类型当然做到了。因此,不,这个集合初始化语法没有创建或添加任何 KeyValuePairs。 - user19858830
从来没有说初始化语法会创建或添加键值对。也许我的回答有点误导人。在幕后,您的Add(string, string)方法将创建一个keyvaluepair<string, IEnumerable<string>>。 - howardButcher

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接