diff options
author | Daniel Grunwald <daniel@danielgrunwald.de> | 2010-10-20 19:58:20 +0400 |
---|---|---|
committer | Daniel Grunwald <daniel@danielgrunwald.de> | 2010-11-01 15:47:49 +0300 |
commit | de0ea567468af7cccc251b8365b52ae868d52604 (patch) | |
tree | 0f3ade4531f962ca604d223cf01421b4486c3fbd /README | |
parent | 2ae70eb79b4c2f8a2ccfec71fd51d0bdbacf42da (diff) |
README: line-break to 100 cols.
Diffstat (limited to 'README')
-rw-r--r-- | README | 345 |
1 files changed, 191 insertions, 154 deletions
@@ -1,154 +1,191 @@ -Overview of the NRefactory library: - -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.Dom: - Abstract Syntax Tree for C# - -ICSharpCode.NRefactory.CSharp.Resolver: - Semantic analysis for C# - -Null-Object pattern: - The NRefactory library makes extensive use of the null object pattern. - As a reult, 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. - -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.) - - 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)) - -A: If you've previously used the .NET Reflection API, the concept of type references is new to you. - - -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 constraint to be a value-type. - 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. +Overview of the NRefactory library:
+
+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.Dom:
+ Abstract Syntax Tree for C#
+
+ICSharpCode.NRefactory.CSharp.Resolver:
+ Semantic analysis for C#
+
+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.
+
+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 constraint to be a value-type.
+ 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.
|