Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are specialized events, fields, methods and properties?

Tags:

c#

roslyn

I'm working on modifying Roslyn, and in this file I come across this code:

ISpecializedMethodReference specializedMethodReference = methodReference.AsSpecializedMethodReference;
if (specializedMethodReference != null)
{
    methodReference = specializedMethodReference.UnspecializedVersion;
}

Following the reference to specializedMethodReference.UnspecializedVersion gets me to this file, and this code:

/// <summary>
/// Represents the specialized event definition.
/// </summary>
internal interface ISpecializedEventDefinition : IEventDefinition
{
    /// <summary>
    /// The event that has been specialized to obtain this event. When the containing type is an instance of type which is itself a specialized member (i.e. it is a nested
    /// type of a generic type instance), then the unspecialized member refers to a member from the unspecialized containing type. (I.e. the unspecialized member always
    /// corresponds to a definition that is not obtained via specialization.)
    /// </summary>
    IEventDefinition/*!*/ UnspecializedVersion
    {
        get;
    }
}

/// <summary>
/// Represents reference specialized field.
/// </summary>
internal interface ISpecializedFieldReference : IFieldReference
{
    /// <summary>
    /// A reference to the field definition that has been specialized to obtain the field definition referred to by this field reference. 
    /// When the containing type of the referenced specialized field definition is itself a specialized nested type of a generic type instance, 
    /// then the unspecialized field reference refers to the corresponding field definition from the unspecialized containing type definition.
    /// (I.e. the unspecialized field reference always refers to a field definition that is not obtained via specialization.)
    /// </summary>
    IFieldReference UnspecializedVersion { get; }
}

/// <summary>
/// Represents reference specialized method.
/// </summary>
internal interface ISpecializedMethodReference : IMethodReference
{
    /// <summary>
    /// A reference to the method definition that has been specialized to obtain the method definition referred to by this method reference. 
    /// When the containing type of the referenced specialized method definition is itself a specialized nested type of a generic type instance, 
    /// then the unspecialized method reference refers to the corresponding method definition from the unspecialized containing type definition.
    /// (I.e. the unspecialized method reference always refers to a method definition that is not obtained via specialization.)
    /// </summary>
    IMethodReference UnspecializedVersion { get; }
}

/// <summary>
/// Represents the specialized property definition.
/// </summary>
internal interface ISpecializedPropertyDefinition : IPropertyDefinition
{
    /// <summary>
    /// The property that has been specialized to obtain this property. When the containing type is an instance of type which is itself a specialized member (i.e. it is a nested
    /// type of a generic type instance), then the unspecialized member refers to a member from the unspecialized containing type. (I.e. the unspecialized member always
    /// corresponds to a definition that is not obtained via specialization.)
    /// </summary>
    IPropertyDefinition/*!*/ UnspecializedVersion
    {
        get;
    }
}

I've read those comments three times, and I've done a bit of Googling, and I have no idea what they're talking about. Could someone please provide me with some explanation, preferably with some examples?

EDIT:

Thanks to the answer by @DaisyShipton and the comments by @HansPassant I now think I have a vague idea of what it's all about. Roslyn optimizes usage of generic fields and methods when possible (calling it specialized), but then to be compatible with the C# metadata standards it has to emit unoptimized metadata (calling it unspecialized).

To test @DaisyShipton's answer I copied the Foo<T> and Bar classes from the answer into a C# program I was using as a test program. Then I modified this bit of Roslyn:

internal BlobHandle GetFieldSignatureIndex(IFieldReference fieldReference)
{
    BlobHandle result;
    ISpecializedFieldReference specializedFieldReference = fieldReference.AsSpecializedFieldReference;
    if (specializedFieldReference != null)
    {
        fieldReference = specializedFieldReference.UnspecializedVersion;
    }

as follows:

internal BlobHandle GetFieldSignatureIndex(IFieldReference fieldReference)
{
    BlobHandle result;
    ISpecializedFieldReference specializedFieldReference = fieldReference.AsSpecializedFieldReference;

   // Added code
   if (fieldReference.Name == "field")
   {
      if (specializedFieldReference == null)
         Console.WriteLine(fieldReference.ToString() + "   Not considered specialized.");
      else
      {
         Console.WriteLine(fieldReference.ToString() + 
                           "   Is considered specialized, converted to: " +
                           specializedFieldReference.UnspecializedVersion.ToString());
      }
   }

    if (specializedFieldReference != null)
    {
        fieldReference = specializedFieldReference.UnspecializedVersion;
    }

Here's the result:

Console window screen shot

I don't understand why this bit of code was hit so many times (9). It can be seen that for the 8 cases where the field reference was considered specialized it's either because int was the known result or the type of T was known to be string. But I can't relate the 9 hits to this code with specific bits of source code in the test program.

like image 984
RenniePet Avatar asked Apr 13 '18 08:04

RenniePet


1 Answers

I believe it's to do with generics, and whether you're dealing with a member declaration within a "general" generic type, or one that already has type arguments. (A "closed, constructed type" in C# language specification terminology, although I've always found the terminology around generics to be tricky.)

Here's an example of the kind of thing I mean, in terms of the typeof operator:

public void Foo<T>()
{
    // We always know what this will be: we're specifying a closed constructed type
    Console.WriteLine(typeof(List<int>));

    // At compile-time, this isn't a closed, constructed type, because
    // it uses a type parameter as the type argument. At execution time,
    // it will take on a closed, constructed type based on the actual type
    // of T for this call.
    Console.WriteLine(typeof(List<T>));

    // This is a generic type definition, with no type argument
    Console.WriteLine(typeof(List<>));
}

Now, to see the equivalent in Roslyn, I'd look at the relevant references here:

public class Foo<T>
{
    public int field;

    public void Method() 
    {
        // I expect this to be an unspecialized reference
        int x = field;
    }
}

public class Bar
{
    public void Method() 
    {
        Foo<string> foo = new Foo<string>();
        // I expect this to be a specialized field reference
        int x = foo.field;
    }
}

The same kind of thing would happen for methods, events etc in generic types. It gets even more complicated with generic methods, potentially, where the method itself can introduce another type parameter...

like image 149
Jon Skeet Avatar answered Sep 22 '22 23:09

Jon Skeet