11 changed files with 630 additions and 39 deletions
@ -0,0 +1,30 @@ |
|||||||
|
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using NUnit.Framework; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.CSharp.Resolver |
||||||
|
{ |
||||||
|
[TestFixture] |
||||||
|
public class UnaryOperatorTests |
||||||
|
{ |
||||||
|
[Test] |
||||||
|
public void TestMethod() |
||||||
|
{ |
||||||
|
//var a = ~new X();
|
||||||
|
char a = 'a'; |
||||||
|
++a; |
||||||
|
a++; |
||||||
|
float b= 1; |
||||||
|
++b; |
||||||
|
b++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class X |
||||||
|
{ |
||||||
|
//public static implicit operator int(X a) { return 0; }
|
||||||
|
public static implicit operator LoaderOptimization(X a) { return 0; } |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,388 @@ |
|||||||
|
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Globalization; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.CSharp.Resolver |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Contains the main resolver logic.
|
||||||
|
/// </summary>
|
||||||
|
public class CSharpResolver |
||||||
|
{ |
||||||
|
static readonly ResolveResult ErrorResult = new ErrorResolveResult(SharedTypes.UnknownType); |
||||||
|
static readonly ResolveResult DynamicResult = new ResolveResult(SharedTypes.Dynamic); |
||||||
|
|
||||||
|
readonly ITypeResolveContext context; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets/Sets whether the current context is <c>checked</c>.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCheckedContext { get; set; } |
||||||
|
|
||||||
|
public CSharpResolver(ITypeResolveContext context) |
||||||
|
{ |
||||||
|
if (context == null) |
||||||
|
throw new ArgumentNullException("context"); |
||||||
|
this.context = context; |
||||||
|
} |
||||||
|
|
||||||
|
#region class OperatorMethod
|
||||||
|
class OperatorMethod : Immutable, IMethod |
||||||
|
{ |
||||||
|
IList<IParameter> parameters = new List<IParameter>(); |
||||||
|
|
||||||
|
public IList<IParameter> Parameters { |
||||||
|
get { return parameters; } |
||||||
|
} |
||||||
|
|
||||||
|
public ITypeReference ReturnType { |
||||||
|
get; set; |
||||||
|
} |
||||||
|
|
||||||
|
IList<IAttribute> IMethod.ReturnTypeAttributes { |
||||||
|
get { return EmptyList<IAttribute>.Instance; } |
||||||
|
} |
||||||
|
|
||||||
|
IList<ITypeParameter> IMethod.TypeParameters { |
||||||
|
get { return EmptyList<ITypeParameter>.Instance; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IMethod.IsExtensionMethod { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IMethod.IsConstructor { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IMethod.IsDestructor { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IMethod.IsOperator { |
||||||
|
get { return true; } |
||||||
|
} |
||||||
|
|
||||||
|
ITypeDefinition IEntity.DeclaringTypeDefinition { |
||||||
|
get { return null; } |
||||||
|
} |
||||||
|
|
||||||
|
ITypeDefinition IMember.DeclaringTypeDefinition { |
||||||
|
get { return null; } |
||||||
|
} |
||||||
|
|
||||||
|
IType IMember.DeclaringType { |
||||||
|
get { return null; } |
||||||
|
} |
||||||
|
|
||||||
|
IMember IMember.MemberDefinition { |
||||||
|
get { return null; } |
||||||
|
} |
||||||
|
|
||||||
|
IList<IExplicitInterfaceImplementation> IMember.InterfaceImplementations { |
||||||
|
get { return EmptyList<IExplicitInterfaceImplementation>.Instance; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IMember.IsVirtual { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IMember.IsOverride { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IMember.IsOverridable { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
EntityType IEntity.EntityType { |
||||||
|
get { return EntityType.Operator; } |
||||||
|
} |
||||||
|
|
||||||
|
DomRegion IEntity.Region { |
||||||
|
get { return DomRegion.Empty; } |
||||||
|
} |
||||||
|
|
||||||
|
DomRegion IEntity.BodyRegion { |
||||||
|
get { return DomRegion.Empty; } |
||||||
|
} |
||||||
|
|
||||||
|
IList<IAttribute> IEntity.Attributes { |
||||||
|
get { return EmptyList<IAttribute>.Instance; } |
||||||
|
} |
||||||
|
|
||||||
|
string IEntity.Documentation { |
||||||
|
get { return null; } |
||||||
|
} |
||||||
|
|
||||||
|
Accessibility IEntity.Accessibility { |
||||||
|
get { return Accessibility.Public; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IEntity.IsStatic { |
||||||
|
get { return true; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IEntity.IsAbstract { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IEntity.IsSealed { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IEntity.IsShadowing { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
bool IEntity.IsSynthetic { |
||||||
|
get { return true; } |
||||||
|
} |
||||||
|
|
||||||
|
IProjectContent IEntity.ProjectContent { |
||||||
|
get { return null; } |
||||||
|
} |
||||||
|
|
||||||
|
string INamedElement.FullName { |
||||||
|
get { return "operator"; } |
||||||
|
} |
||||||
|
|
||||||
|
string INamedElement.Name { |
||||||
|
get { return "operator"; } |
||||||
|
} |
||||||
|
|
||||||
|
string INamedElement.Namespace { |
||||||
|
get { return string.Empty; } |
||||||
|
} |
||||||
|
|
||||||
|
string INamedElement.DotNetName { |
||||||
|
get { return "operator"; } |
||||||
|
} |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ResolveUnaryOperator
|
||||||
|
public ResolveResult ResolveUnaryOperator(UnaryOperatorType op, ResolveResult expression) |
||||||
|
{ |
||||||
|
if (expression.Type == SharedTypes.Dynamic) |
||||||
|
return DynamicResult; |
||||||
|
|
||||||
|
// C# 4.0 spec: §7.3.3 Unary operator overload resolution
|
||||||
|
string overloadableOperatorName = GetOverloadableOperatorName(op); |
||||||
|
if (overloadableOperatorName == null) { |
||||||
|
switch (op) { |
||||||
|
case UnaryOperatorType.Dereference: |
||||||
|
PointerType p = expression.Type as PointerType; |
||||||
|
if (p != null) |
||||||
|
return new ResolveResult(p.ElementType); |
||||||
|
else |
||||||
|
return ErrorResult; |
||||||
|
case UnaryOperatorType.AddressOf: |
||||||
|
return new ResolveResult(new PointerType(expression.Type)); |
||||||
|
default: |
||||||
|
throw new ArgumentException("Invalid value for UnaryOperatorType", "op"); |
||||||
|
} |
||||||
|
} |
||||||
|
// If the type is nullable, get the underlying type:
|
||||||
|
IType type = NullableType.GetUnderlyingType(expression.Type); |
||||||
|
bool isNullable = type != null; |
||||||
|
if (!isNullable) |
||||||
|
type = expression.Type; |
||||||
|
|
||||||
|
// the operator is overloadable:
|
||||||
|
// TODO: implicit support for user operators
|
||||||
|
//var candidateSet = GetUnaryOperatorCandidates();
|
||||||
|
|
||||||
|
UnaryOperatorMethod[] methodGroup; |
||||||
|
switch (op) { |
||||||
|
case UnaryOperatorType.Increment: |
||||||
|
case UnaryOperatorType.Decrement: |
||||||
|
case UnaryOperatorType.PostIncrement: |
||||||
|
case UnaryOperatorType.PostDecrement: |
||||||
|
// C# 4.0 spec: §7.6.9 Postfix increment and decrement operators
|
||||||
|
// C# 4.0 spec: §7.7.5 Prefix increment and decrement operators
|
||||||
|
TypeCode code = ReflectionHelper.GetTypeCode(type); |
||||||
|
if (type.IsEnum() || (code >= TypeCode.SByte && code <= TypeCode.Decimal)) |
||||||
|
return new ResolveResult(expression.Type); |
||||||
|
else |
||||||
|
return ErrorResult; |
||||||
|
case UnaryOperatorType.Plus: |
||||||
|
methodGroup = unaryPlusOperators; |
||||||
|
break; |
||||||
|
case UnaryOperatorType.Minus: |
||||||
|
methodGroup = IsCheckedContext ? checkedUnaryMinusOperators : uncheckedUnaryMinusOperators; |
||||||
|
break; |
||||||
|
case UnaryOperatorType.Not: |
||||||
|
methodGroup = logicalNegationOperator; |
||||||
|
break; |
||||||
|
case UnaryOperatorType.BitNot: |
||||||
|
if (type.IsEnum()) { |
||||||
|
if (expression.IsCompileTimeConstant && expression.ConstantValue != null) { |
||||||
|
// evaluate as (E)(~(U)x);
|
||||||
|
var U = expression.ConstantValue.GetType().ToTypeReference().Resolve(context); |
||||||
|
var unpackedEnum = new ConstantResolveResult(U, expression.ConstantValue); |
||||||
|
return ResolveCast(expression.Type, ResolveUnaryOperator(op, unpackedEnum)); |
||||||
|
} else { |
||||||
|
return new ResolveResult(expression.Type); |
||||||
|
} |
||||||
|
} else { |
||||||
|
methodGroup = bitwiseComplementOperators; |
||||||
|
break; |
||||||
|
} |
||||||
|
default: |
||||||
|
throw new InvalidOperationException(); |
||||||
|
} |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
object GetUserUnaryOperatorCandidates() |
||||||
|
{ |
||||||
|
// C# 4.0 spec: §7.3.5 Candidate user-defined operators
|
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
static string GetOverloadableOperatorName(UnaryOperatorType op) |
||||||
|
{ |
||||||
|
switch (op) { |
||||||
|
case UnaryOperatorType.Not: |
||||||
|
return "op_LogicalNot"; |
||||||
|
case UnaryOperatorType.BitNot: |
||||||
|
return "op_OnesComplement"; |
||||||
|
case UnaryOperatorType.Minus: |
||||||
|
return "op_UnaryNegation"; |
||||||
|
case UnaryOperatorType.Plus: |
||||||
|
return "op_UnaryPlus"; |
||||||
|
case UnaryOperatorType.Increment: |
||||||
|
case UnaryOperatorType.PostIncrement: |
||||||
|
return "op_Increment"; |
||||||
|
case UnaryOperatorType.Decrement: |
||||||
|
case UnaryOperatorType.PostDecrement: |
||||||
|
return "op_Decrement"; |
||||||
|
default: |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
abstract class UnaryOperatorMethod : OperatorMethod |
||||||
|
{ |
||||||
|
public abstract object Invoke(object input); |
||||||
|
} |
||||||
|
|
||||||
|
sealed class LambdaUnaryOperatorMethod<T> : UnaryOperatorMethod |
||||||
|
{ |
||||||
|
readonly Func<T, T> func; |
||||||
|
|
||||||
|
public LambdaUnaryOperatorMethod(Func<T, T> func) |
||||||
|
{ |
||||||
|
this.func = func; |
||||||
|
} |
||||||
|
|
||||||
|
public override object Invoke(object input) |
||||||
|
{ |
||||||
|
return func((T)input); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
sealed class LiftedUnaryOperatorMethod : UnaryOperatorMethod |
||||||
|
{ |
||||||
|
UnaryOperatorMethod baseMethod; |
||||||
|
|
||||||
|
public LiftedUnaryOperatorMethod(UnaryOperatorMethod baseMethod) |
||||||
|
{ |
||||||
|
this.baseMethod = baseMethod; |
||||||
|
this.ReturnType = NullableType.Create(baseMethod.ReturnType); |
||||||
|
this.Parameters.Add(new DefaultParameter(NullableType.Create(baseMethod.Parameters[0].Type), string.Empty)); |
||||||
|
} |
||||||
|
|
||||||
|
public override object Invoke(object input) |
||||||
|
{ |
||||||
|
if (input == null) |
||||||
|
return null; |
||||||
|
else |
||||||
|
return baseMethod.Invoke(input); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static UnaryOperatorMethod[] Lift(params UnaryOperatorMethod[] methods) |
||||||
|
{ |
||||||
|
UnaryOperatorMethod[] lifted = new UnaryOperatorMethod[methods.Length * 2]; |
||||||
|
methods.CopyTo(lifted, 0); |
||||||
|
for (int i = 0; i < methods.Length; i++) { |
||||||
|
lifted[methods.Length + i] = new LiftedUnaryOperatorMethod(methods[i]); |
||||||
|
} |
||||||
|
return lifted; |
||||||
|
} |
||||||
|
|
||||||
|
// C# 4.0 spec: §7.7.1 Unary plus operator
|
||||||
|
static readonly UnaryOperatorMethod[] unaryPlusOperators = Lift( |
||||||
|
new LambdaUnaryOperatorMethod<int>(i => +i), |
||||||
|
new LambdaUnaryOperatorMethod<uint>(i => +i), |
||||||
|
new LambdaUnaryOperatorMethod<long>(i => +i), |
||||||
|
new LambdaUnaryOperatorMethod<ulong>(i => +i), |
||||||
|
new LambdaUnaryOperatorMethod<float>(i => +i), |
||||||
|
new LambdaUnaryOperatorMethod<double>(i => +i), |
||||||
|
new LambdaUnaryOperatorMethod<decimal>(i => +i) |
||||||
|
); |
||||||
|
|
||||||
|
// C# 4.0 spec: §7.7.2 Unary minus operator
|
||||||
|
static readonly UnaryOperatorMethod[] uncheckedUnaryMinusOperators = Lift( |
||||||
|
new LambdaUnaryOperatorMethod<int>(i => unchecked(-i)), |
||||||
|
new LambdaUnaryOperatorMethod<long>(i => unchecked(-i)), |
||||||
|
new LambdaUnaryOperatorMethod<float>(i => -i), |
||||||
|
new LambdaUnaryOperatorMethod<double>(i => -i), |
||||||
|
new LambdaUnaryOperatorMethod<decimal>(i => -i) |
||||||
|
); |
||||||
|
static readonly UnaryOperatorMethod[] checkedUnaryMinusOperators = Lift( |
||||||
|
new LambdaUnaryOperatorMethod<int>(i => checked(-i)), |
||||||
|
new LambdaUnaryOperatorMethod<long>(i => checked(-i)), |
||||||
|
new LambdaUnaryOperatorMethod<float>(i => -i), |
||||||
|
new LambdaUnaryOperatorMethod<double>(i => -i), |
||||||
|
new LambdaUnaryOperatorMethod<decimal>(i => -i) |
||||||
|
); |
||||||
|
|
||||||
|
// C# 4.0 spec: §7.7.3 Logical negation operator
|
||||||
|
static readonly UnaryOperatorMethod[] logicalNegationOperator = Lift(new LambdaUnaryOperatorMethod<bool>(b => !b)); |
||||||
|
|
||||||
|
// C# 4.0 spec: §7.7.4 Bitwise complement operator
|
||||||
|
static readonly UnaryOperatorMethod[] bitwiseComplementOperators = Lift( |
||||||
|
new LambdaUnaryOperatorMethod<int>(i => ~i), |
||||||
|
new LambdaUnaryOperatorMethod<uint>(i => ~i), |
||||||
|
new LambdaUnaryOperatorMethod<long>(i => ~i), |
||||||
|
new LambdaUnaryOperatorMethod<ulong>(i => ~i) |
||||||
|
); |
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ResolveCast
|
||||||
|
public ResolveResult ResolveCast(IType targetType, ResolveResult expression) |
||||||
|
{ |
||||||
|
if (expression.IsError) |
||||||
|
return new ErrorResolveResult(targetType); |
||||||
|
|
||||||
|
// C# 4.0 spec: §7.7.6 Cast expressions
|
||||||
|
if (expression.IsCompileTimeConstant) { |
||||||
|
TypeCode code = ReflectionHelper.GetTypeCode(targetType); |
||||||
|
if (code >= TypeCode.Boolean && code <= TypeCode.Decimal) { |
||||||
|
try { |
||||||
|
return new ConstantResolveResult(targetType, Convert.ChangeType(expression.ConstantValue, code, CultureInfo.InvariantCulture)); |
||||||
|
} catch (InvalidCastException) { |
||||||
|
return new ErrorResolveResult(targetType); |
||||||
|
} |
||||||
|
} else if (code == TypeCode.String) { |
||||||
|
if (expression.ConstantValue == null || expression.ConstantValue is string) |
||||||
|
return new ConstantResolveResult(targetType, expression.ConstantValue); |
||||||
|
else |
||||||
|
return new ErrorResolveResult(targetType); |
||||||
|
} else if (targetType.IsEnum()) { |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
} |
||||||
|
return new ResolveResult(targetType); |
||||||
|
} |
||||||
|
#endregion
|
||||||
|
} |
||||||
|
} |
||||||
@ -1,22 +1,29 @@ |
|||||||
|
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
using System; |
using System; |
||||||
using ICSharpCode.NRefactory.TypeSystem; |
using ICSharpCode.NRefactory.TypeSystem; |
||||||
|
|
||||||
namespace ICSharpCode.NRefactory.CSharp.Resolver |
namespace ICSharpCode.NRefactory.CSharp.Resolver |
||||||
{ |
{ |
||||||
public class ConstantResolveResult |
/// <summary>
|
||||||
|
/// ResolveResult representing a compile-time constant.
|
||||||
|
/// </summary>
|
||||||
|
public class ConstantResolveResult : ResolveResult |
||||||
{ |
{ |
||||||
public IType Type { get; set; } |
object constantValue; |
||||||
public object Value { get; set; } |
|
||||||
|
|
||||||
public ConstantResolveResult(IType type, object value) |
public ConstantResolveResult(IType type, object constantValue) : base(type) |
||||||
{ |
{ |
||||||
if (type == null) |
this.constantValue = constantValue; |
||||||
throw new ArgumentNullException("type"); |
} |
||||||
if (value == null) |
|
||||||
throw new ArgumentNullException("value"); |
public override bool IsCompileTimeConstant { |
||||||
this.Type = type; |
get { return true; } |
||||||
this.Value = value; |
} |
||||||
|
|
||||||
|
public override object ConstantValue { |
||||||
|
get { return constantValue; } |
||||||
} |
} |
||||||
} |
} |
||||||
} |
} |
||||||
|
|||||||
@ -0,0 +1,22 @@ |
|||||||
|
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.CSharp.Resolver |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Represents a resolve error.
|
||||||
|
/// </summary>
|
||||||
|
public class ErrorResolveResult : ResolveResult |
||||||
|
{ |
||||||
|
public ErrorResolveResult(IType type) : base(type) |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsError { |
||||||
|
get { return true; } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,40 @@ |
|||||||
|
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.CSharp.Resolver |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Represents the result of resolving an expression.
|
||||||
|
/// </summary>
|
||||||
|
public class ResolveResult |
||||||
|
{ |
||||||
|
IType type; |
||||||
|
|
||||||
|
public ResolveResult(IType type) |
||||||
|
{ |
||||||
|
if (type == null) |
||||||
|
throw new ArgumentNullException("type"); |
||||||
|
this.type = type; |
||||||
|
} |
||||||
|
|
||||||
|
public IType Type { |
||||||
|
get { return type; } |
||||||
|
} |
||||||
|
|
||||||
|
public virtual bool IsCompileTimeConstant { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
|
||||||
|
public virtual object ConstantValue { |
||||||
|
get { return null; } |
||||||
|
} |
||||||
|
|
||||||
|
public virtual bool IsError { |
||||||
|
get { return false; } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,62 @@ |
|||||||
|
// Copyright (c) 2010 AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||||
|
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.NRefactory.TypeSystem.Implementation; |
||||||
|
|
||||||
|
namespace ICSharpCode.NRefactory.TypeSystem |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Static helper methods for working with nullable types.
|
||||||
|
/// </summary>
|
||||||
|
public static class NullableType |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Gets whether the specified type is a nullable type.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsNullable(IType type) |
||||||
|
{ |
||||||
|
return GetUnderlyingType(type) != null; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the element type, if <paramref name="type"/> is a nullable type.
|
||||||
|
/// Otherwise, returns null.
|
||||||
|
/// </summary>
|
||||||
|
public static IType GetUnderlyingType(IType type) |
||||||
|
{ |
||||||
|
ParameterizedType pt = type as ParameterizedType; |
||||||
|
if (pt != null && pt.TypeArguments.Count == 1 && pt.FullName == "System.Nullable") |
||||||
|
return pt.TypeArguments[0]; |
||||||
|
else |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a nullable type.
|
||||||
|
/// </summary>
|
||||||
|
public static IType Create(IType elementType, ITypeResolveContext context) |
||||||
|
{ |
||||||
|
if (elementType == null) |
||||||
|
throw new ArgumentNullException("elementType"); |
||||||
|
if (context == null) |
||||||
|
throw new ArgumentNullException("context"); |
||||||
|
|
||||||
|
ITypeDefinition nullable = context.GetClass("System.Nullable", 1, StringComparer.Ordinal); |
||||||
|
if (nullable != null) |
||||||
|
return new ParameterizedType(nullable, new [] { elementType }); |
||||||
|
else |
||||||
|
return SharedTypes.UnknownType; |
||||||
|
} |
||||||
|
|
||||||
|
static readonly ITypeReference NullableReference = new GetClassTypeReference("System.Nullable", 1); |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a nullable type reference.
|
||||||
|
/// </summary>
|
||||||
|
public static ITypeReference Create(ITypeReference elementType) |
||||||
|
{ |
||||||
|
return new ParameterizedTypeReference(NullableReference, new [] { elementType }); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue