forked from icsharpcode/NRefactory
-
Notifications
You must be signed in to change notification settings - Fork 0
UnicoenProject/NRefactory
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Overview of the NRefactory library: ICSharpCode.Editor: Interfaces that abstract from the text editor control used. Maybe future AvalonEdit versions will directly implement these interfaces, but you could also write adapters for other editors. ICSharpCode.NRefactory.TypeSystem: Contains a language-independent representation of the .NET type system. ICSharpCode.NRefactory.TypeSystem.Implementation: Contains base classes that help implementing the type system interfaces. ICSharpCode.NRefactory.CSharp.Ast: Abstract Syntax Tree for C# ICSharpCode.NRefactory.CSharp.Resolver: Semantic analysis for C# (resolving the meaning of expressions) ICSharpCode.NRefactory.CSharp.Analysis: Semantic analysis for C# (additional analysis functionality) ICSharpCode.NRefactory.Documentation: Classes for working with .xml documentation files. ICSharpCode.NRefactory.PatternMatching: Provides classes for pattern matching over the C# and VB ASTs. ICSharpCode.NRefactory.Util: Various helper classes. ICSharpCode.NRefactory.VB.Ast: (in the separate ICSharpCode.NRefactory.VB assembly) Abstract Syntax Tree for VB Dependencies: .NET 3.5 or .NET 4.0 Mono.Cecil 0.9.4 Null-Object pattern: The NRefactory library makes extensive use of the null object pattern. As a result, NullReferenceExceptions should be very rare when working with this library. In the type system, both ITypeReference and IType use SharedTypes.UnknownType to represent unknown types. Unless the method is documented otherwise, no method or property returning a ITypeReference or IType will return null. When adding to this library, to try to keep such uses of null rare. Note that the null object pattern is not used for ITypeDefinition: IProjectContent.GetClass() returns null when a type is not found. Take care to abort your operation or substitute UnknownType instead of passing the null to code expecting an IType. The pattern also extends to the C# resolver, which always produces a ResolveResult, even in error cases. Use ResolveResult.IsError to detect resolver errors. Also note that many resolver errors still have a meaningful type attached, this allows code completion to work in the presence of minor semantic errors. The C# AST makes use of special null nodes when accessing the getter of an AST property and no child node with that role exists. Check the IsNull property to test whether a node is a null node. Null nodes are not considered to be part of the AST (e.g. they don't have a parent). FAQ: Q: What is the difference between types and type definitions? A: Basically, a type (IType) is any type in the .NET type system: - an array (ArrayType) - a pointer (PointerType) - a managed reference (ByReferenceType) - a parameterized type (ParameterizedType, e.g. List<int>) - a type parameter (ITypeParameter, e.g. T) - or a type definition (ITypeDefiniton) Type definitions are only classes, structs, enums and delegates. Every type definition is a type, but not every type is a type definition. NRefactory's ITypeDefinition derives from IType, so you can directly use any type definition as a type. In the other direction, you could try to cast a type to ITypeDefinition, or you can call the GetDefinition() method. The GetDefinition() method will also return the underlying ITypeDefinition if given a parameterized type, so "List<int>".GetDefinition() is "List<T>". Q: What is the difference between types and type references? I've seen lots of duplicated classes (ArrayType vs. ArrayTypeReference, etc.) A: If you've previously used the .NET Reflection API, the concept of type references will be new to you. NRefactory has the concept of the "project content": every assembly/project is stored independently from other assemblies/projects. It is possible to load some source code into a project which contains the type reference "int[]" without having to load mscorlib into NRefactory. So inside the entities stored for the project, the array type is only referenced using an ITypeReference. This interface has a single method: interface ITypeReference { IType Resolve(ITypeResolutionContext context); } By calling the Resolve()-method, you will get back the actual ArrayType. At this point, you have to provide the type resolution context: Note that every type can also be used as type reference - the IType interface derives from ITypeReference. Every IType simply returns itself when the Resolve()-method is called. Types are often directly used as references when source and target of the reference are within the same assembly. Because an ArrayType must have an IType as element type, we also need the ArrayTypeReference to represent an array of a type that's not yet resolved. When resolved, the ArrayTypeReference produces an array type: new ArrayTypeReference(r).Resolve(context) = new ArrayType(r.Resolve(context)) Q: What's in an ITypeResolveContext? A: An ITypeResolveContext is an environment for looking up namespaces and types. Usually, a resolve context will represent a set of projects. Most of the time, that set will be the current project, plus the direct references of the current project. Every project content on its own is a type resolve context (IProjectContent extends ITypeResolveContext); but (with the exception of mscorlib) isn't useful for resolving types as you also need the references. To represent a set of projects, the class CompositeTypeResolveContext can be used. Q: How do I get the IType or ITypeReference for a primitive type such as string or int? A: Please use: TypeCode.Int32.ToTypeReference().Resolve(context) Skip the Resolve() call if you only need the type reference. ReflectionHelper.ToTypeReference is very fast if given a TypeCode (it simply looks up an existing type reference in an array), and your code will benefit from caching of the resolve result (once that gets implemented for these primitive type references). Avoid using "context.GetClass(typeof(int))" - this call involves Reflection on the System.Type being passed, cannot benefit from any caching implemented in the future, and most importantly: it may return null. And do you always test your code in a scenario where mscorlib isn't contained in the resolve context? The approach suggested above will return SharedTypes.UnknownType when the type cannot be resolved, so you don't run into the risk of getting NullReferenceExceptions. Q: Is it thread-safe? A: This question is a bit difficult to answer. NRefactory was designed to be usable in a multi-threaded IDE. But of course, this does not mean that everything is thread-safe. First off, there's no hidden static state, so any two operations working on independent data can be executed concurrently. [Actually, sometimes static state is used for caches, but those uses are thread-safe.] TODO: what about the C# parser? gmcs is full of static state... Some instance methods may use hidden instance state, so it is not safe to e.g use an instance of the CSharp.Resolver.Conversions class concurrently. Instead, you need to create an instance on every thread. In the case of project contents, it is desirable to be able to use them, and all the classes in that project content, on multiple threads - for example, to provide code completion in an IDE while a background thread parses more files and adds them to the project content. For this reason, the entity interfaces (ITypeDefinition, IMember, etc.) are designed to be freezable. Once the Freeze() method is called, an entity instance becomes immutable and thread-safe. Whether an ITypeResolveContext is thread-safe depends on the implementation: TypeStorage: thread-safe for concurrent reads, but only if it's not written to (see XML documentation on TypeStorage) CecilProjectContent: immutable and thread-safe SimpleProjectContent: fully thread-safe CompositeTypeResolveContext: depends on the child contexts Usually, you'll work with a set of loaded projects (SimpleProjectContents) and loaded external assemblies (CecilProjectContent). A CompositeTypeResolveContext representing such a set is thread-safe. Hoever, some algorithms can become confused if two GetClass() calls with same arguments produce different results (e.g. because another thread updated a class definition). Also, there's a performance problem: if you have a composite of 15 SimpleProjectContents and the resolve algorithm requests 100 types, that's 1500 times entering and leaving the read-lock. Moreoever, internal caches in the library are not used when passing a mutable ITypeResolveContext. The solution is to make the read lock more coarse-grained: using (var syncContext = compositeTypeResolveContext.Synchronize()) { resolver.ResolveStuff(syncContext); } On the call to Synchronize(), all 15 SimpleProjectContents are locked for reading. The return value "syncContext" can then be used to access the type resolve context without further synchronization overhead. It is guaranteed not to change (within the using block), so the library may cache some information. (TODO: give example of a cache) Once the return value is disposed, the read-locks are released (and the caches are cleared). Q: What format do the .ToString() methods use? A: They don't use any particular format. They're merely intended as a debugging aid. Currently .ToString() usually matches .ReflectionName, but that may change in the future. Q: Why are there extension methods IType.IsEnum() and IType.IsDelegate(), but no IType.IsStruct() or IType.IsInterface()? A: Because if you're asking whether a type is a struct, it's very likely that you're asking the wrong question. The distinction between class/struct/interface/enum/delegate is important in the world of type definitions, and there's ITypeDefinition.ClassType to address this. But the distinction isn't so important in the world of types. If whatever you are doing works with struct-types, then it likely will also work with enum-types, and also with type parameters with a value-type constraint. So instead of asking IsStruct(), you really should be asking: IType.IsReferenceType == false Enums and delegates are special because you can do special things with those types (e.g. subtract them from each other). If you really need to know, you can do "type.GetDefinition() != null && type.GetDefinition().ClassType == WhatIWant" yourself, but for the most part you should be fine with IsReferenceType, IsEnum and IsDelegate. Q: What's the difference between the .NET 3.5 and .NET 4.0 versions? A: As for visible API difference, not much. The .NET 4.0 build has some additional overloads for a few methods, taking a System.Threading.CancellationToken to allow aborting a resolve run. Internally, the .NET 4.0 version might be tiny bit more performant because it uses covariance for IEnumerable, where the .NET 3.5 version has to allocate wrapper objects instead. Both versions support loading assemblies of all .NET versions (1.0 to 4.0); and both support C# 4.0.
About
Repository for future NRefactory version
Resources
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published
Languages
- C# 100.0%