view Implab.ServiceHost/Unity/TypeReference.cs @ 278:6691aff01de1 v3

Implab: added XmlDefaultSeializer (SerializersPool is now obsolete) Implab.ServiceHost: rewritten TypeReference (added support for nested types), stable API
author cin
date Thu, 03 May 2018 09:59:44 +0300
parents 0be8a6ae8307
children
line wrap: on
line source

using System;
using System.Linq;
using System.Text;

namespace Implab.ServiceHost.Unity {
    /// <summary>
    /// Ссылка на тип, является абстрактной записью имени CLR типа.
    /// </summary>
    /// <remarks>
    /// Ссылка на тип содержит сокращенную информацию о типе и для ее интерпретации
    /// требуется некоторый контекст, который позволит превратить ее в полноценный
    /// <see cref="Type"/>. Ссылки на тип позволяют записать:
    /// <list>
    ///     <item><description>общие типы, их специализации</description></item>
    ///     <item><description>вложенные типы</description></item>
    ///     <item><description>массивы</description></item>
    /// </list>
    /// <para>
    /// Для получения из ссылки на тип конкретного CLR типа используется <see cref="TypeResolver.Resolve(TypeReference, bool)"/>.
    /// </para>
    /// <para>
    /// Ссылку на тип можно создать либо програмно при помощи методов <see cref="Create(string, string, int)"/>,
    /// <see cref="Create(string, int)"/>, <see cref="MakeArrayType(int)"/>, <see cref="MakeGenericType(TypeReference[])"/>,
    /// либо разобрав строку со спецификацией при помощи метода <see cref="Parse(string)"/>.
    /// </para>
    /// <para>
    /// Спецификация ссыдки на тип имеет следующий вид <c>Name.Space.MyType+Nested{String}[][]</c>, где:
    /// <list type="table">
    ///     <item>
    ///         <term><c>.</c></term>
    ///         <description>Разделяет элементы пространства имен</description>
    ///     <item>
    ///     <item>
    ///         <term><c>+</c></term>
    ///         <description>Разделяет вложенные типы</description>
    ///     <item>
    ///     <item>
    ///         <term><c>[]</c>, <c>[,,,]</c></term>
    ///         <description>Указывает на то, что тип является массивом, также указывается его размерность</description>
    ///     <item>
    ///     <item>
    ///         <term><c>{}</c>, <c>{,,}</c>, <c>{Int32,String}</c></term>
    ///         <description>Указывает на то, что тип является общим, также 
    ///         указывается количество параметров, либо конкретные типы для
    ///         специализации</description>
    ///     <item>
    /// </list>
    /// </para>
    /// </remarks>
    public abstract class TypeReference {

        /// <summary>
        /// Имя типа без дополнительных элементов, указывающих на то, что он общий или массив.
        /// </summary>
        /// <remarks>
        /// Для массивов это имя его элементов.
        /// </remarks>
        public abstract string Name { get; }

        /// <summary>
        /// Пространство имен в котором нахожится тип.
        /// </summary>
        /// <remarks>
        /// Для вложенных типов это пространтство имен типа самого верхнего уровня,
        /// для массивов - пространство имен его элементов.
        /// </remarks>
        public abstract string Namespace { get; }

        /// <summary>
        /// Количество параметров общего типа.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Вложенные типы неявно получают параметры от типов в которых они объявлены,
        /// данное свойство это не учитывает, возвращается только количество собственных
        /// параметров.
        /// </para>
        /// <para>
        /// Данное свойство используется для получения CRL имени типа.
        /// </para>
        /// </remarks>
        public abstract int GenericParametersCount { get; }

        public virtual string ClrName {
            get {
                return GenericParametersCount != 0 ? $"{Name}`{GenericParametersCount}" : Name;
            }
        }

        /// <summary>
        /// Создает ссылку на специализацию текущего типа.
        /// </summary>
        /// <param name="genericParameters">Ссылки на типы, которые будут использоваться для специализации текущего типа.</param>
        /// <returns>Специализация данного типа.</returns>
        public virtual SpecializedTypeReference MakeGenericType(TypeReference[] genericParameters) {
            if (GenericParametersCount == 0)
                throw new InvalidOperationException("Can't specialize a non-geneic type");

            if (genericParameters == null || GenericParametersCount != genericParameters.Length)
                throw new InvalidOperationException("Generic parameters count mismatch");

            return new SpecializedTypeReference(this, genericParameters);
        }

        /// <summary>
        /// Создает ссылку на тип массива указанной размерности, элементами которого являются экземпаляры даннго типа.
        /// </summary>
        /// <param name="rank">Размерность, если размерность <c>1</c> создается вектор (<see cref="Type.MakeArrayType()"/>).</param>
        /// <returns>Ссылка на тип массива</returns>
        public ArrayTypeReference MakeArrayType(int rank) {
            Safe.ArgumentInRange(rank > 0, nameof(rank));

            return new ArrayTypeReference(this, rank);
        }

        /// <summary>
        /// Создает ссылку на вложенный тип.
        /// </summary>
        /// <param name="name">Имя типа</param>
        /// <param name="genericParameters">Количество параметров, если это общий тип, иначе 0.</param>
        /// <returns>Ссылка на вложенный тип.</returns>
        public TypeReference Create(string name, int genericParameters) {
            Safe.ArgumentNotEmpty(name, nameof(name));
            Safe.ArgumentInRange(genericParameters >= 0, nameof(genericParameters));

            return new NestedTypeReference(this, name, genericParameters);
        }

        /// <summary>
        /// Возвращает строковое представление ссылки на тип.
        /// </summary>
        /// <returns></returns>
        public override string ToString() {
            var builder = new StringBuilder();
            WriteTypeName(builder);
            WriteTypeParams(builder);
            return builder.ToString();
        }

        internal virtual void WriteTypeName(StringBuilder builder) {
            if (!string.IsNullOrEmpty(Namespace))
                builder
                    .Append(Namespace)
                    .Append('.');
            builder.Append(Name);
        }

        internal virtual void WriteTypeParams(StringBuilder builder) {
            if (GenericParametersCount > 0)
                builder
                    .Append('{')
                    .Append(',', GenericParametersCount-1)
                    .Append('}');
        }

        internal abstract void Visit(TypeResolutionContext visitor);

        /// <summary>
        /// Создает ссылку на тип.
        /// </summary>
        /// <param name="ns">Пространство имен, либо его фрагмент.</param>
        /// <param name="name">Имя типа без указания на количество параметров, либо на то, что это массив.</param>
        /// <param name="genericParameters">Количество параметров типа, если это общий тип, иначе 0.</param>
        /// <returns>Ссылка на тип.</returns>
        public static TypeReference Create(string ns, string name, int genericParameters) {
            Safe.ArgumentNotEmpty(name, nameof(name));
            Safe.ArgumentInRange(genericParameters >= 0, nameof(genericParameters));
            return new RootTypeReference(ns, name, genericParameters);
        }

        /// <summary>
        /// Разирает строковую запись ссылки на тип.
        /// </summary>
        /// <param name="typeSpec">Строковая запись ссылки на тип, например <c>Dictionary{String,String}</c></param>
        /// <returns>Ссылка на тип.</returns>
        public static TypeReference Parse(string typeSpec) {
            var parser = new TypeReferenceParser(typeSpec);
            return parser.Parse();
        }

    }
}