CodeType get generic parameters - c#

We're using the Visual Studio CodeModel and have some problems to get the generic parameters of a CodeType. How to obtain them without parsing the FullName ourselves?
It is hinted (although not marked an answer) in How can I get the generic constraints from CodeInterface as a CodeType object? that there is no other way, however, this is not really believable as:
System.Func<Outer.Inner>
would not be defined: You cannot know if the generic parameter you've parsed (Outer.Inner) is referring to the namespace Outer containing a class Inner or if it is referring to the class Outer having an inner class Inner (and yes, it is not Outer+Inner in such cases).
If somebody at least knows how to tell the FullName property to show nested classes with a + sign this would be great too.

I think the answer here is pretty definitive. This isn't supported by DTE or DTE2 and is unlikely to be supported in the future.
The only way currently is to use Roslyn, which is not acceptable for those of us that don't want to use pre-release software. I also haven't looked into what kinds of dependencies that will entail (do users of my component need to install Roslyn?).
You could use a regular expression to get the types from the FullName string. But, for those of us in the real world who need the token (T) to concrete type (System.String) mapping, this is not an option.

I can't find a way to do it for any generic type, but if you need to do it for a specific type, it is possible in some cases.
For instance, I have the following code to check if a type is a collection, and if it is, get the element type:
private static bool IsCollectionType(CodeType type, out CodeType elementType)
{
// string implements IEnumerable<char>, but we don't want to treat it as a collection
if (type.FullName == "System.String")
{
elementType = null;
return false;
}
var enumerable = type.Bases.OfType<CodeInterface>().FirstOrDefault(i => i.FullName.StartsWith("System.Collections.Generic.IEnumerable<"));
var method = enumerable?.Members.OfType<CodeFunction>().FirstOrDefault(m => m.Name == "GetEnumerator");
var enumerator = method?.Type.CodeType;
var current = enumerator?.Members.OfType<CodeProperty>().FirstOrDefault(m => m.Name == "Current");
if (current != null)
{
elementType = current.Type.CodeType;
return true;
}
elementType = null;
return false;
}
As you can see, I'm not directly looking at the generic type argument, but instead I look at the type of IEnumerable<T>.GetEnumerator().Current. Of course, this requires specific knowledge about the type you're working with.

Related

Is it possible to create an anonymous generic object?

Imagine that you want to declare an object with an anonymous type, but you need it to be generic (I can't think of an actual reason why, this is theoretical). How would you create such an object?
For example:
var anonymousGeneric = new <T>{ }; // <- doesn't work
var anonymousGeneric = Activator.CreateInstance((new { }).GetType()); // Not generic
edit:
// because:
(new { }).GetType().IsGenericType == false
// Of course, any useful anonymous object _will_ be generic:
(new { a="b" }).GetType().IsGenericType == true
// But in the process of testing various aspects of this question
// it had never occurred to me that I needed to supply any property
// (this was all theoretical, remember)
end edit
But neither of those works. Of course, the real-world solution to this imagined problem is to define an actual class definition:
public GenericThing<T> { }
But that isn't anonymous like above.
Once the object is created, imagine using it later on with something like:
var anonymousGenericType = anonymousGeneric.GetType();
// These throw exception
// <>f__AnonymousType0#1 is not a GenericTypeDefinition. MakeGenericType may only be called on a type for which Type.IsGenericTypeDefinition is true.
// + System.RuntimeType.MakeGenericType(System.Type[])
var intThing = anonymousGenericType.MakeGenericType(typeof(int));
var stringThing = anonymousGenericType.MakeGenericType(typeof(string));
In summary, is it possible to create an anonymous generic object?
(I can't think of an actual reason why, this is theoretical)
In that case, let's stick with the simple and obvious, since it's easier to find a reason: simply create a regular anonymous object from a generic method.
public object Foo<T>(T t) { return new { t }; }
Here, Foo(0) and Foo("") will necessarily return different types, but they'll still share a type definition.
Pretty much any use of anonymous types can make equal sense inside a generic method.
anonymousGeneric.GetType() is returning the wrong type of generic type: Closed (with type parameter(s)) vs open (without)1. If you want to change a type parameter, you need to get the generic type definition from it.
The following actually works, though I can't imagine what good it does anybody 2:
var anonymousGeneric = new {a = "b"};
var anonymousGenericType = anonymousGeneric.GetType().GetGenericTypeDefinition();
var intThingType = anonymousGenericType.MakeGenericType(typeof(int));
var intThingInstance = Activator.CreateInstance(intThingType, 9);
Now we have a thing just like anonymousGeneric, but its type parameter is int instead of string, and its a property is 9. But what's it good for? How do you declare a reference to it? You could bind to its properties in XAML, if you had some time on your hands.
1 Thanks to Amy and Servy for pitching in to clear up my confusion with terminology.
2 Note that there are more things in heaven and earth than are dreamt of in my philosophy.

Getting type from a symbol in roslyn

What is the best general purpose way to get a System.Type from Microsoft.CodeAnalysis.ISymbol for different types of symbols ? (e.g. class declarations, variable, properties, etc)
I want to be able to do various checks on the type e.g. as checking if the type implements any interface or is cast-able to any interface, just like one can check on System.Type.
The problem I am having is that most of the concrete classes used to represent the symbol are internal (see http://sourceroslyn.io/) and I could not find tye type information in the ISymbol.
SourceNamedTypeSymbol
LocalSymbol
I retrieve the ISymbol using the following code
var objectSymbol = (ISymbol)model.GetDeclaredSymbol(obj.Node);
Short answer: you can't. There is no proper way to get a System.Type (reflection) from an ISymbol (Roslyn).
One option to do go in the direction you want is constructing the fully-qualified name of your type and then looking that up through reflection (example).
You should probably ask yourself whether this is something you need to do in the first place though -- reflection and Roslyn aren't really intended to work together.
What you are interested in, however, can be done through Roslyn as well. The key here is using the semantic model which has all this information for you.
All declarations (opposed to usages) have a specific overload available that allows you to get the declaring symbol and return it in the appropriate type (such as INamedTypeSymbol in this case).
Take the following example:
const string source = #"
using System;
namespace MyNamespace
{
class MyClass : IDisposable
{
void Method()
{
MyClass nameOfVariable, another;
}
}
}
";
var tree = CSharpSyntaxTree.ParseText(source);
var compilation = CSharpCompilation.Create("MyCompilation", new[] { tree }, new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location) });
var semanticModel = compilation.GetSemanticModel(tree);
var root = tree.GetRoot();
var classSymbol = semanticModel.GetDeclaredSymbol(root.DescendantNodes().OfType<ClassDeclarationSyntax>().First());
Console.WriteLine(string.Join(", ", classSymbol.AllInterfaces));
This will display all the interfaces the class implements. Keep in mind however that this just refers to the current definition -- if you're also interested in base types you'll have to go through the hierarchy yourself.
In your scenario you should be able to just cast it to the right type (assuming you are checking a declaration node):
var objectSymbol = (INamedTypeSymbol) model.GetDeclaredSymbol(obj.Node);
I think this is what you are looking for:
var castedProperty = (IPropertySymbol) property;
var type = castedProperty.Type.Name;
The variable 'property' is an ISymbol instance.

How to compare a Microsoft.CodeAnalysis.ITypeSymbol to a System.Type

I have successfully received an ITypeSymbol from a SyntaxNode by using:
SemanticModel.GetTypeInfo(sytaxNode).ConvertedType
Now I would like to know if this ITypeSymbol corresponds to a System.Type instance that is present in my executing code, like typeof(IEnumerable<int>) or someObject.GetType().
I tried
typeInfo.ConvertedType.ToString() == type.ToString()
But these do not use the same formatting rules, for instance for generics like IEnumerable<int>
TypeInfo.ToString() == "System.Collections.Generic.IEnumerable<int>"
while
typeof(IEnumerable<int>).ToString() == "System.Collections.Generic.IEnumerable`1[System.Int32]"
Moreover I think it would be better to compare AssemblyQualifiedNames in stead of just the Namespace and type name to avoid possible name clashes.
Ideally I would like to be able to get the actual System.Type instance in my executing code that corresponds to the ITypeInfo I got from the semantic model (provided that the required assembly is loaded and/or available). That would allow checking if the type is assignable from some other type etc.
You can get the INamedTypeSymbol for a type name with Compilation.GetTypeByMetadataName().
So try this:
semanticModel.GetTypeInfo(sytaxNode).ConvertedType.Equals(
semanticModel.Compilation.GetTypeByMetadataName(typeof(WhateverType).FullName))
This won't work with closed generic types, for those you'll need to do a bit more. For example:
var ienumerableType = semanticModel.Compilation.GetTypeByMetadataName("System.Collections.Generic.IEnumerable`1");
var intType = semanticModel.Compilation.GetTypeByMetadataName("System.Int32");
var type = ienumerableType.Construct(intType);
Based on the answer from #Tamas, I created the following recursive solution that works for closed generic types.
static bool TypeSymbolMatchesType(ITypeSymbol typeSymbol, Type type, SemanticModel semanticModel)
{
return GetTypeSymbolForType(type, semanticModel).Equals(typeSymbol);
}
static INamedTypeSymbol GetTypeSymbolForType(Type type, SemanticModel semanticModel)
{
if (!type.IsConstructedGenericType)
{
return semanticModel.Compilation.GetTypeByMetadataName(type.FullName);
}
// get all typeInfo's for the Type arguments
var typeArgumentsTypeInfos = type.GenericTypeArguments.Select(a => GetTypeSymbolForType(a, semanticModel));
var openType = type.GetGenericTypeDefinition();
var typeSymbol = semanticModel.Compilation.GetTypeByMetadataName(openType.FullName);
return typeSymbol.Construct(typeArgumentsTypeInfos.ToArray<ITypeSymbol>());
}

Picking objects of a given type from a List

I have a Type object. I want to find every object in a List that is of this type. Is there something like list.OfType<> using a Type object? Or do I have to use it this way:
list.Where(obj => obj.GetType() == type) // corrected thanks to jmcilhinney
This will be heavily used in my application, so I need to find a solution that is as fast as possible.
You need to call Where:
var items = myList.Where(item => item.GetType() == myType);
You can't use OfType<T> in the case where you have the type stored in a variable. Remember that, with generics, the type parameter is a compile-time constant. Therefore you can't use a generic function by specifying T in a variable, which is not known until runtime.
To look at this another way, there are two possible variants of your function:
public IEnumerable<T> GetOfType<T>(IEnumerable<object> list)
{
return list.OfType<T>();
}
public IEnumerable<object> GetOfType(IEnumerable<object> list, Type type)
{
return list.All(obj => obj != null && obj.GetType() == type);
}
To use the generic version, ultimately some consumer of the function is going to need to supply the type as a compile-time constant.

Using reflection with generic types and implicit conversions

I'm trying to use reflection to set properties on some OpenXML types (e.g. Justification). Assigning a value by enumerating all possibilities is straight-forward:
// attr is an XmlAttribute, so .Name and .Value are Strings
if (attr.Name == "Val")
{
if (element is Justification)
{
((Justification)element).Val = (JustificationValues)Enum.Parse(typeof(JustificationValues), attr.Value);
return;
}
else
{
// test for dozens of other types, such as TabStop
}
}
What makes this difficult to do via reflection is:
1) The type of the Val property is EnumValue<T>, so I don't know how to extract the type to pass as the first argument to Enum.Parse.
2) There is an implicit conversion from the actual enumeration type to the EnumValue<> type, which I don't know how to invoke with reflection.
I would like the code to end up looking something like:
PropertyInfo pInfo = element.GetType().GetProperty(attr.Name);
Object value = ConvertToPropType(pInfo.PropertyType, attr.Value); /* this
would return an instance of EnumValue<JustificationValues> in this case */
pInfo.SetValue(element, value, null);
How do I implement ConvertToPropType? Or is there a better solution?
Thanks
Edit:
I got a solution working using Earwicker's suggestion, but it relies on the convenient fact that the enumeration's type name can be derived from the node's type name ("Justification" -> "JustificationValues"). I'm still curious how to solve this in the general case, though.
Edit2:
GetGenericArguments got me the rest of the way there. Thanks.
If the attribute value is just a string, I assume you already have some way of figuring out that the string identifies a value from a specific enumeration. In your example you have it hard-coded, so I'm not sure if that's what you want or what you want to change.
Assuming you know it's an enumeration, and you know which enumeration, you already know how to get an object containing a boxed value of the right enum type, as in your snippet.
Now if I assume EnumValue<T> has a constructor that takes a T.
Type genericType = typeof(EnumValue<>);
Type concreteType = genericType.MakeGenericType(typeof(JustificationValues));
Now concreteType is the type EnumValue<JustificationValues>.
From that you can get a constructor, hopefully one that takes a JustificationValues parameter, and Invoke it.
Update
Ahh, I see what you're doing now. You use the XML attribute name to pick a C# property. You need to be able to detect whether that property is of a type EnumValue<T>, and find out what T is.
PropertyInfo p = // ... get property info
Type t = p.GetType();
if (t.IsGenericType &&
t.GetGenericTypeDefinition == typeof(EnumValue<>))
{
Type e = t.GetGenericArguments()[0]; // get first (and only) type arg
// e is the enum type...
Give that a try.
.Net 4.0 adds support to do a late bound implict or explict conversion. This is simplified in the open source framework ImpromptuInterface with it's static method called InvokeConvert. In your ideal example it would work like this:
PropertyInfo pInfo = element.GetType().GetProperty(attr.Name);
Object value = Impromptu.InvokeConvert(attr.Value, pInfo.PropertyType);
pInfo.SetValue(element, value, null);
This might only work with basic types but it was good enough for what I'm doing
PropertyInfo pInfo = element.GetType().GetProperty(attr.Name);
Object value = System.Convert.ChangeType(attr.Value, pInfo.PropertyType);
pInfo.SetValue(element, value, null);

Categories