New "Orcas" Language Feature: Anonymous Types

Over the last two months I've published a series of posts covering some of the new language features that are coming as part of the Visual Studio and .NET Framework "Orcas" release.  Here are pointers to the first four posts in my series:

Today's blog post covers the last new feature in my language series: Anonymous Types. 

What are Anonymous Types?

Anonymous types are a convenient language feature of C# and VB that enable developers to concisely define inline CLR types within code, without having to explicitly define a formal class declaration of the type.

Anonymous types are particularly useful when querying and transforming/projecting/shaping data with LINQ.

Anonymous Type Example

In my previous Query Syntax blog post I demonstrated how you could transform data with projections. This is a powerful feature of LINQ that enables you to perform query operations on a data source (regardless of whether it is a database, an XML file, or an in-memory collection), and shape the results of the data being queried into a different structure/format than the original data source is in.

In my previous Query Syntax blog post I defined a custom "MyProduct" class that I used to represent my transformed product data.  By explicitly defining the "MyProduct" class I have a formal CLR type contract that I can use to easily pass my custom-shaped product results between web-services or between multiple classes/assemblies within my application solution.

However, there are times when I just want to query and work with data within my current code scope, and I don't want to have to formally define an explicit class that represents my data in order to work with it.  This is where anonymous types are very useful, as they allow you to concisely define a new type to use inline within your code. 

For example, assume I use the LINQ to SQL object relational mapper designer within "Orcas" to model the "Northwind" database with classes like below:

 

I can then use the below code to query the Product data in my database, and use the projection/transformation capability of LINQ to custom shape the data result to be something other than the "Product" class above.  Rather than use an explicitly defined "MyProduct" class to represent each custom-shaped row of data retrieved from the database, I can instead use the anonymous type feature to implicitly define a new type with 4 properties to represent my custom shaped data like so:

In the code above I'm declaring an anonymous type as part of the select clause within my LINQ expression, and am having the compiler automatically create the anonymous type with 4 properties (Id, Name, UnitPrice and TotalRevenue) - whose property names and type values are inferred from the shape of the query. 

I'm then using the new "var" keyword within C# to programmatically refer to the IEnumerable<T> sequence of this anonymous type that is returned from the LINQ expression, as well as to refer to each of the anonymous type instances within this sequence when I programmatically loop over them within a foreach statement later in my code.

While this syntax gives me dynamic language-like flexibility, I also still retain the benefits of a strongly-typed language - including support for compile-time checking and code intellisense within Visual Studio.  For example, notice above how I am doing a foreach over the returned products sequence and I am still able to get full code intellisense and compilation checking on the anonymous type with custom properties that was inferred from the LINQ query.

Understanding the Var Keyword

C# "Orcas" introduces a new var keyword that may be used in place of the type name when performing local variable declarations. 

A common misperception that people often have when first seeing the new var keyword is to think that it is a late-bound or un-typed variable reference (for example: a reference of type Object or a late-bound object like in Javascript).  This is incorrect -- the var keyword always generates a strongly typed variable reference.  Rather than require the developer to explicitly define the variable type, though, the var keyword instead tells the compiler to infer the type of the variable from the expression used to initialize the variable when it is first declared.

The var keyword can be used to reference any type in C# (meaning it can be used with both anonymous types and explictly declared types).  In fact, the easiest way to understand the var keyword is to look at a few examples of it using common explict types.  For example, I could use the var keyword like below to declare three variables:

The compiler will infer the type of the "name", "age" and "male" variables based on the type of their initial assignment value (in this case a string, an integer, and a boolean).  This means it will generate IL that is absolutely identical to the code below:

The CLR actually never knows that the var keyword is being used - from its perspective there is absolutely no difference between the above two code examples.  The first version is simply syntactic sugar provided by the compiler that saves the developer some keystrokes, and has the compiler do the work of inferring and declaring the type name.

In addition to using built-in datatypes with the var keyword, you can obviously also use any custom types you define.  For example, I could go back to the LINQ query projection I did in my previous blog post that used an explicit "MyProduct" type for the data-shaping and adapt it to use the var keyword like so:

Important: Although I'm using the "var" keyword above, I'm not using it with an anonymous type.  My LINQ query is still shaping the returned data using the "MyProduct" type - which means that the "var products" declaration is simply a shorthand for "IEnumerable<Product> products".  Likewise, the "var p" variable I defined within my foreach statement is simply shorthand for a a variable of type "MyProduct p". 

Important Rule about the Var Keyword

Because the var keyword produces a strongly-typed variable declaration, the compiler needs to be able to infer the type to declare based on its usage.  This means that you need to always do an initial value assignment when declaring one. The compiler will produce a compiler error if you don't:

Declaring Anonymous Types

Now that we've introduced the "var" keyword, we can start to use it to refer to anonymous types.

Anonymous types in C# are defined using the same object initializer syntax I covered in my first blog post in this language series.  The difference is that instead of declaring the type-name as part of the initialization grammar, when instantiating anonymous types you instead just leave the type-name blank after the "new" keyword:

The compiler will parse the above syntax and automatically define a new standard CLR type that has 4 properties.  The types of each of the 4 properties are determined based on the type of the initialization values being assigned to them (for example: in the sample above the "Id" property is being assigned an integer - so the compiler will generate the property to be of type integer). 

The actual CLR name of the anonymous type will automatically be generated by the C# compiler.  The CLR itself actually doesn't know the difference between an anonymous type and a named type - so the runtime semantics of the two are absolutely identical.  Bart De Smet has a good blog post here that details this if you want to see the exact class name pattern and IL generated. 

Note above how when you type "product." on the anonymous type, you still get compile-time checking and full intellisense within Visual Studio.  Notice also how the intellisense description indicates it is an "AnonymousType" - but still provides full declaration information of the properties (this is the text circled in red).

Using Anonymous Types for Hierarchical Shaping

One of the powerful scenarios that anonymous types makes easy is the ability to easily perform hierarchical shape projections of data with a minimum amount of code. 

For example, I could write the below LINQ expression to query all products from the Northwind database whose price is greater than $50, and then shape the returned products in a hierarchical structure sorted by the Products' stock reorder level (using the "group into" clause supported by LINQ query syntax):

When the above code is run in ASP.NET, I'll get the below output rendered in my browser:

I could likewise do nice hierarchical shapings based on JOIN results.  For example, the below code creates a new anonymous type with some standard product column properties, as well as a hierarchical sub-collection property that contains the orderdetails of the 5 most recent orders that customers have placed for that particular product:

Notice how I can neatly traverse the hierarchical data.  Above I'm looping over the product query, and then drilling into the collection of the last 5 orders for each product.  As you can see, I have full intellisense and compile-time checking everywhere (even on properties of objects within the nested sub-collection of order details on the anonymous type).

Data Binding Anonymous Types

As I mentioned earlier in this blog post, there is absolutely no difference from a CLR perspective between an anonymous type and an explicitly defined/named type.  Anonymous types and the var keyword are purely "syntactic sugar" that avoid you having to type code - the runtime semantics are the same as using explicitly defined types. 

Among other things, this means that all of the standard .NET type reflection features work with anonymous types - which means that features like databinding to UI controls work just fine with them.  For example, if I wanted to display the results of my previous hierarchical LINQ query, I could define an <asp:gridview> control within a .aspx page like below:

The .aspx above contains a gridview with 2 standard boundfield columns, and one templated field column that contains a nested <asp:bulletedlist> control that I'll use to display the product's hierarchical orderdetail sub-results. 

I could then write the below LINQ code to perform my hierarchical query against the database and databind the custom-shaped results against the GridView to display:

Because the GridView supports binding against any IEnumerable<T> sequence, and uses reflection to retrieve property values, it will work just fine against the anonymous type I'm using above. 

At runtime the above code will produce a simple grid of product details with a hierarchical list of their recent order quantities like so:

Obviously you could make this report much richer and prettier - but hopefully you get the idea of how easy it is to now perform hierarchical queries against a database, shape the returned results however you want, and then either work against the results programmatically or databind them to UI controls.

Summary

Anonymous types are a convenient language feature that enable developers to concisely define inline CLR types within code, without having to explicitly provide a formal class declaration of the type.  Although they can be used in lots of scenarios, there are particularly useful when querying and transforming/shaping data with LINQ. 

This post concludes my 5-part language series for "Orcas".  Going forward I'll be doing many more LINQ posts that will demonstrate how to actually take advantage of all of these new language features to perform common data access operations (defining data models, querying, updating, using sprocs, validation, etc).  I wanted to get this 5 part language series done first, though, so that you'll have a good way to really understand the underlying language constructs as we drill into scenarios within my upcoming posts.

Hope this has helped,

Scott

Published Tuesday, May 15, 2007 7:02 AM by ScottGu
Filed under: , , ,

Comments

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 10:12 AM by tshao

Fantastic! C#Script! :)

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 10:23 AM by Dugald Wilson

Scott,

I've been dying to watch your Orcas session from Mix, but it seems to be one of the few pieces that aren't coming out. Any idea on if it will see daylight?

Thanks,

Dugald Wilson

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 10:36 AM by Matt

Anything we need to be aware about when using anonymous types against generics?

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 10:38 AM by cm

thanks scott!  another excellent post!

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 10:44 AM by ScottGu

Hi Matt,

You can use the var keyword with any type of variable - so it will work just fine against a generic collection.

One of the nice things about LINQ itself is that it builds on top of all of the generics features (in fact - it wouldn't work without it).

So when you do a LINQ query, but default you get back a generic-based IEnumerable<T> sequence as a result.  If you call ToList() on your LINQ expression, it will return back a generics based List<T> collection.

The <T> type in the scenarios above can be an anonymous type (in fact that is what my samples are doing above).

Hope this helps,

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 11:52 AM by Ryan

Great post Scott!

Does is scare you that features like this will create lazy programers that create all variables with var?

I can see the power of it, I can also see the power of "laziness"... but then again that's probably up for those coding teams to figure out :D

I'm getting excited for this release!

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 11:53 AM by Peter

I imagine there are some restrictions on passing "var" parameters. For example: How would this work?

int Add(var p1, var p2)

{

 return p1 + p2;

}

if you call it like this:

int sum = Add(2,"4x");

Thanks.

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 2:01 PM by Ponnu

Hi Scott,

Great Stuff at Mix especially with Silverlight, Anomymous Types-another cool stuff to look forward to Orcas

This a question I wanted to ask you on a different topic.

Is it possible to change the way the ASP.Net's MemberShip,Profiles,etc... Tables(aspnet_*), views and sp's are named as in our company we follow a different naming convention for DB objects. At the least I am looking at changing the schema owner from dbo to aspnet(as that's how we name in our company like Category.TableName instead of dbo.TableName).Any help on this will be greatly appreciated.

Thanks

Ponnu

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 2:07 PM by ahmed

Dugald- Scott's session at MIX never happened - he did the keynote. Maybe check Omar Khan's session from Mix

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 2:59 PM by Mike

How do you get IntelliSense to understand this? Is it constantly compiling code in the background, because it must pick up the type as soon as possible. Just curious.

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 3:11 PM by Rick Strahl

This is one of the coolest LINQ features I think, but boy do I wish you could pass the Anonymous type (var) out of local method scope. Without that you can't use these types inside of a business layer that needs to pass the result back to the UI layer.

Given that the compiler creates the type already why not support some syntax to expose that type publicly? Isn't there any chance this can still be changed? <s>

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 3:33 PM by Alex G

I would recommend to avoid using `var` for anything other than dynamic types. If you know the resulting type, declare it.

Also, isn't entity framework out of orcas? Why still demo it? :P

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 3:51 PM by ScottGu

Hi Ryan,

It is a good question (does not having to declare a type make you lazy).  In general I'd recommend using this feature primarily for dynamic anonymous types - and/or for the result of LINQ queries.  

What I like about the "var" keyword (and LINQ in general) is that it provides a nice blend of dynamic functionality in a strongly-typed world.  This seems to combine some of the best attributes of both the dynamic and the static models, and is one I'm really enjoying coding with.

Hope this helps,

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 3:52 PM by ScottGu

Alex - LINQ to SQL is definitely still in the "Orcas" release.  So all of the data samples I've done in my blog posts to-date definitely still work.

Hope this helps,

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 3:54 PM by ScottGu

Hi Peter,

Unfortunately you can't use the "var" keyword for parameter arguments.  The reason is that var is strongly-typed, so the compiler needs to see the initialization value to infer the type.

What you can do, though, and which I should blog more about, is fully leverage generics with classes and method arguments.  This gives you some really nice options for handling parameters (and works with VS 2005 and .NET 2.0 today).

Hope this helps,

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 4:40 PM by Ryan Ternier

Hey Scott,

Thanks for the reply.  Can the var keyword be used as a return type for a function... such as:

var myThing = GetAThing(parameters...);

...

public var GetAThing(string parameter)

{

...

}

GetAThing could return any number of things: int, string, double, custom objects etc.

Is that possible? Or is it stretching it?

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 5:17 PM by Jim

Scott,

I would like to select one record and check if it is null.  I remember reading that var cannot be null.  Any ideas?

var user = (from u in db.Users

                        where u.Email == email && u.Password == password

                        select new { u.UserId, }).Take(1);

Keep up the good work on your blog!

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 6:01 PM by Jason Collins

I wish the "var" declaration could be limited to only places where an anonymous type is required.

I fear code like this:

 var x = Some.Function.Call();

This says to me: "I'm too lazy to look it up, and now no one knows for sure, including the code reviewer, maintainer, etc." There is a serious readability problem.

That said, when using it for legit anonymous types, the feature absolutely rocks!

# re: New "Orcas" Language Feature: Anonymous Types

Tuesday, May 15, 2007 6:21 PM by Steve Muench

Scott, great post. One quick question. How is it that you can conclude from the code you've shown that p.OrderDetails.Take(5) returns the *LAST* five orders. That is, where is the implicit "order by order date descending" baked into the example? That was something I didn't understand from the code you've shown here. Thanks for any light you can shed on this.

# re: New "Orcas" Language Feature: Anonymous Types

Wednesday, May 16, 2007 12:14 AM by Vikram

Yes its good to see these new features in C# langauge

# re: New "Orcas" Language Feature: Anonymous Types

Wednesday, May 16, 2007 1:08 AM by ScottGu

Hi Ryan,

Unfortunately you can't use the var keyword for return types.  

Sorry!

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Wednesday, May 16, 2007 1:13 AM by ScottGu

Hi Steve,

With regard to your question:

>>> "How is it that you can conclude from the code you've shown that p.OrderDetails.Take(5) returns the *LAST* five orders. That is, where is the implicit "order by order date descending" baked into the example? That was something I didn't understand from the code you've shown here. Thanks for any light you can shed on this."

That is a good question - and depending on how your database is organized my statement that it is the last 5 orders might not be correct.

A more complete way of writing this would be:

var products = from p in db.Products

              where p.UnitPrice > 50

              select new

              {

                 Id = p.ProductID,

                 Name = p.ProductName,

                 LastFiveOrders = p.OrderDetails.OrderByDescending(o=>o.Order.OrderDate).Take(5)

              };

Hope this helps,

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Wednesday, May 16, 2007 1:17 AM by ScottGu

Hi Jim,

In regards to your question:

>>> I would like to select one record and check if it is null.  

You can use the FirstOrDefault() extension method with any LINQ expression to retrieve the first result in the sequence - or return null if the sequence is empty.

For example:

var user = db.Users.Where(u=>u.Email == email).Select(u=>u.UserId).FirstOrDefault();

if (user == null) {

 // do something

}

Hope this helps,

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Wednesday, May 16, 2007 1:21 AM by ScottGu

Hi Pablo,

When using WCF (or any other remoting technology), I'd recommend not using the var keyword - and instead explictly define the types you want to use.

This will allow you to use the type contracts more easily across remoting or assembly boundaries.

Hope this helps,

Scott

P.S. We'll definitely have architecture documents on how to best apply LINQ to SQL and the other new "Orcas" features in the future.  Stay tuned!

# re: New "Orcas" Language Feature: Anonymous Types

Wednesday, May 16, 2007 9:54 AM by ScottGu

Hi Boris,

There are a couple of different approaches you can take.  Assuming you are just retrieving a join of existing entities, you can rely on the existing relationships between Customers and Orders.

For example, you could return:

List<Customer> GetCustomerOrders(customerId) {

   // do join to return customers with orders populated

}

This would then allow you to access the "Orders" property on each customer object and drill into the orders.  This property could be pre-populated in your method, or lazily done based on the first access.

If you want to create a join with dynamic new properties that don't exist in the entity and pass it between tiers/web-services/class boundaries, then you'll want to define an explict class to use (check out my first automatic properties post that describes ways this is now easier in "Orcas").

Hope this helps,

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Wednesday, May 16, 2007 12:11 PM by Josh Stodola

Hi Scott,

Having a background in Javascript makes this new var keyword kinda exciting for me :)

I started to agree with what was commented on earlier about this feature resulting in "lazy programmers".  I got to thinking about this... and, quite frankly, we (programmers) are lazy!  It's our style, that is why our jobs consist of sitting on our ass and watching an LCD rather than digging a trench.  Upon revisiting the concept of laziness, I dont think this will make us lazy at all.  The word for this is efficient.  We won't be lazy, but we will be more efficient by utilizing this new keyword.

Thanks for another great post, Scott.

On a totally unrelated note, could you tell me why the www.asp.net web site/forums are still slower than molasses?  Since you are the fearless leader of the ASP.NET team(s), I think you should be a little offended at how poorly that site performs.  I really think it is giving new and upcoming developers a biased outlook on the overall performance of ASP.NET.  Now, I know that the problem is not with ASP.NET, it is with the hardware serving that site.  But, new and upcoming developers certainly would not realize that.  I have brought this issue up a countless number of times on the forums, but nothing gets done about it.  What do you think?

# re: New "Orcas" Language Feature: Anonymous Types

Wednesday, May 16, 2007 12:19 PM by ScottGu

Hi Josh,

The www.asp.net site is in the process of being upgraded today.  Some of the servers were pretty old (5 years in some cases), and at the same time some site layout/UI is being updated.

Unfortunately the team doing it has run into a few issues (hardware, network + software), which is why the site is running slow now.  People are definitely working on getting this fixed, though, so hopefully it will be better soon.

Hope this helps,

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Wednesday, May 16, 2007 12:44 PM by Orlando Agostinho

Hi, Scott

Great Post!! I have a few questions for you, about LINQ:

1) Can i ask if is it possible to use "SQL hints" with LINQ? In that way, How about diferences between Sql Server 2005, Oracle, etc?

2) SQL generated by LINQ have attention if the  database is Sql Server 2000, Oracle, DB2,etc?

3)  Is it possible reuse LINQ Queries? Imagine, i have one LINQ query that'll be reused in 2 or 3 classes. Normally, inside Business Layer. Is it that possible?

4) LINQ + Transactions?  How about!

See Ya!

Orlando Agostinho

Lisbon/Portugal

# re: New "Orcas" Language Feature: Anonymous Types

Wednesday, May 16, 2007 12:58 PM by ScottGu

Hi Orlando,

1) You can optionally drop down and tweak the raw SQL for queries if you want to.  You can also use SPROCs and Views inside the database with LINQ to SQL.

2) There will be a provider model in the future that allows you to use LINQ against multiple different types of databases.

3) The nice thing about LINQ queries is that they are variables that you can easily re-use/share across classes.  Because LINQ queries are executed on demand when you access the result-set, this makes it really easy to pass them around (and only execute them when needed).

4) Yep - you can definitely use transactions with LINQ.  By default when you make changes an implicit transaction is wrapped around all updates.  You can also then declare an explicit transaction to scope many things into a broader transaction.

Hope this helps,

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Thursday, May 17, 2007 8:29 AM by Andy Barker

Looks awesome Scott! One thing I'm curious about though: with LINQ providing all of the flexibility it does, will it now be possible for the ASP.Net gridview control to sort IEnumerable<T> data out-of-the-box now? I always hated that you could bind against a collection, VS would detect the right types through reflection, and would do automatic AJAX-style paging, but it couldn't do the clean client-side sorting. Please, please, tell me that this will now be possible! :)

# re: New "Orcas" Language Feature: Anonymous Types

Thursday, May 17, 2007 3:00 PM by cats

Thanks for the post.

# re: New "Orcas" Language Feature: Anonymous Types

Thursday, May 17, 2007 3:09 PM by Jeff Brown

I'm afraid this will probably be my least used new language feature in C# 3.0.  Why?  Because I cannot perform ExtractMethod on the body of a method that contains an anonymous type...  This is somewhat problematic to say the least.

Basically the syntax is creating an instance of a new type but there isn't a way to express that type in the code.  So we're stuck with type-inference based compiler magic and the "var" keyword.  It sucks because it's not really compositional any more.

If I could somehow specify the shape of that anonymous tuple type in the code then it would make for a much more interesting feature.  Like this:

struct { int CustomerId, string Name } customer = GetCustomerInfo(...);

# re: New "Orcas" Language Feature: Anonymous Types

Friday, May 18, 2007 2:17 AM by ScottGu

Hi Andy,

If you use an ObjectDataSource and have your GridView point at that, you can do in-memory paging/sorting on the data.

In Orcas Beta2 we'll also have a <asp:linqdatasource> control that will support binding against any LINQ to SQL entity, and enable the Grid to support automatic paging/sorting with that.

Hope this helps,

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Friday, May 18, 2007 2:20 AM by ScottGu

Hi Jeff,

One of the cool refactoring features that you'll be able to have eventually is the ability to right-click on an anonymous type definition, and cause it to create an explicit type.  

This gives you an elegant way to start off with an anonymous type, and then quickly refactor to a contract that you can pass around.

VS Orcas probably won't have this factoring built-in at RTM - but it is something I know we will eventually have (and I'm sure third party refactoring tools will have even quicker).

Hope this helps,

Scott

# re: New "Orcas" Language Feature: Anonymous Types

Friday, May 18, 2007 4:35 PM by psan

Hi, is it possible to use the System.Data.Linq dll in 2.0 projects? Without full trust.

# re: New "Orcas" Language Feature: Anonymous Types

Monday, May 28, 2007 10:10 AM by Rab

Hi Scott,

Quick Question: In the query below, is LastFiveOrders of type IEnumerable<Order> and are all the properties of order retreived. What if we wanted to retrieve only the quantity property of order. How would be rewrite the query.

select new

{

Id = p.ProductID,

Name = p.ProductName,

LastFiveOrders = p.OrderDetails.Take(5)

};

Regards,

# re: New "Orcas" Language Feature: Anonymous Types

Thursday, June 14, 2007 7:36 AM by ikann

this is a great new feature

# re: New "Orcas" Language Feature: Anonymous Types

Thursday, June 14, 2007 7:38 AM by ikann

how long does it take for comments to go up

Thanks for sharing your feedback! If your feedback doesn't appear right away, please be patient as it may take a few minutes to publish - or longer if the blogger is moderating comments.