Тип C # Generics при использовании этого ключевого слова - PullRequest
2 голосов
/ 12 марта 2012

Объекты, которые я собираю, позволяют объекту выставлять себя как нечто, что может быть преобразовано другим объектом того же типа. Подумайте в духе слияния, но операции не являются общими / простыми, так что вы можете использовать отражение, чтобы просто получить все свойства и выполнить некоторый заранее определенный набор операций. Поэтому я решил попытаться реализовать его как объекты, которые выставляют себя как Transformable и прикрепляют к ним объект, который знает, как выполнить преобразование (которое можно взаимозаменять). У меня есть компилируемый код:

public interface ITransformer<TType> where TType : class
{
    void Transform(TType source, TType destination);
}

public interface ITransformable<TType> where TType : class
{
    bool Locked { get; set; }
    ITransformer<TType> Transformer { get; set; }

    void TransformFrom(TType source);
}

public class MyTransformer : ITransformer<MyTransformable>
{
    public void Transform(MyTransformable source, MyTransformable destination)
    {
        // Perform some logic for the transform
    }
}

public class MyTransformable : ITransformable<MyTransformable>
{
    public bool Locked { get; set; }
    public ITransformer<MyTransformable> Transformer { get; set; }

    public string Value1 { get; set; }
    public uint Value2 { get; set; }
    public bool Value3 { get; set; }

    public void TransformFrom(MyTransformable source)
    {
        Transformer.Transform(source, this);
    }
}

Как видите, тип MyTransformable содержит некоторые данные (Value1 - Value3), а также состояние Locked и объект Transformer будет знать, как выполнить преобразование для этого элемента (допускаются только общие типы, обеспечивающие работу только трансформаторов, способных работать с типом MyTransformer ).

Моя проблема в том, что я не хочу, чтобы все новые объекты типа Transformable должны были повторять вызов

public void TransformFrom(MyTransformable source)
{
    Transformer.Transform(source, this);
}

Итак, я надеялся, что смогу изменить объект MyTransformable на

public abstract class Transformable<TType> : ITransformable<TType> where TType : class
{
    public bool Locked { get; set; }
    public ITransformer<TType> Transformer { get; set; }

    public void TransformFrom(TType source)
    {
        Transformer.Transform(source, this);  // ERROR! this is type Transformable<TType> not TType
    }
}

public class MyTransformable : Transformable<MyTransformable>
{

}

Но, конечно, это не скомпилируется из-за ошибки, которую я выделил. Я чувствую, что упустил точку где-то вдоль линии. Кто-нибудь может указать мне правильное направление на это?

Ответы [ 3 ]

1 голос
/ 13 марта 2012

Что вам нужно сделать, это добавить хук для производного типа, чтобы представить окончательную, реальную TType реализацию (или просто this). Лучше всего это сделать через абстрактное свойство.

public abstract class Transformable<TType> : ITransformable<TType>
{
    public bool Locked { get; set; }
    public ITransformer<TType> Transformer { get; set; }

    protected abstract TType Value { get; }

    public void TransformFrom(TType source)
    {
        Transformer.Transform(source, Value); 
    }
}

public class MyOtherTransformable : Transformable<MyTransformable>
{
    protected override MyTransformable Value
    {
        get { return this; }
    }
}
1 голос
/ 13 марта 2012

Если вы используете .NET 4, вы можете использовать приведение к dynamic:

public void TransformFrom(TType source)
{
    Transformer.Transform(source, (dynamic)this);
}

Это позволяет CLR выполнять сопоставление типов параметров во время выполнения на основе реального типа (закрытый универсальный) объекта во время выполнения, предотвращая ошибку во время компиляции.Во время выполнения CLR может легко определить, что source и this относятся к одному типу, и будет выполнять вызов.

С другой стороны, это решает вашу проблему.Недостатком является то, что использование dynamic является изначально рефлексивным процессом, который сначала медленнее, а во-вторых может скрывать ошибки, которые вызывают исключения во время выполнения.

0 голосов
/ 13 марта 2012

Это приведет к большему количеству кастований, чем вы, вероятно, можете себе позволить, и даже может победить цель того, что вы пытаетесь сделать, но это скомпилирует:

public interface ITransformable { }

public interface ITransformer<TType>
    where TType: ITransformable
{
    void Transform(ITransformable source, ITransformable destination);
}

public interface ITransformable<TType> : ITransformable
    where TType: ITransformable
{
    bool Locked { get; set; }
    ITransformer<TType> Transformer { get; set; }

    void TransformFrom(TType source);
}

public class MyTransformer : ITransformer<MyTransformable>
{
    public void Transform(ITransformable source, ITransformable destination)
    {
        if (source.GetType() != destination.GetType())
            throw new InvalidOperationException();

        // Perform some logic for the transform
    }
}

public abstract class Transformable<TType> : ITransformable<TType> where TType: ITransformable
{
    public bool Locked { get; set; }
    public ITransformer<TType> Transformer { get; set; }

    public void TransformFrom(TType source)
    {
        Transformer.Transform(source, this);
    }
}

public class MyTransformable : Transformable<MyTransformable>
{

}
...