Pergunta
No C # 3.0 e .NET 3.5, imaginar que há uma interface:
public interface INameable
{
string Name {get;}
}
e muitas classes imutáveis ??que implementam a interface.
Eu gostaria de ter um único método de extensão
public static T Rename<T>(this T obj) where T : INameable
{
...
}
que retorna uma instância envolto do objeto original com apenas o nome alterado e todas as outras propriedades lê e chamadas de método encaminhado para o objeto original.
Como obter uma classe wrapper genérico para isso, sem implementá-lo para todos os tipos de aplicação INameable? Você acha que é possível?
Solução
Não, isso não é possível, a menos que T
é obrigado a ser uma interface ou para ser uma classe com todos os membros virtuais, e essa restrição não é possível especificar em tempo de compilação (embora você poderia escrever um cheque de tempo de execução se você está feliz com essa abordagem).
A razão que você não pode fazer isso para tipos arbitrários é que se você tomar um objeto do tipo T
então você deve retornar um objeto que pode ser atribuído a T
. Se eu passar um objeto que tem o seguinte aspecto ...
public sealed class SomeClass : INameable
{
public string Name { get; }
public string Abc { get; }
}
... então não há nenhuma maneira você pode criar outro tipo que é atribuído a SomeClass
. Não usando Reflection.Emit
ou qualquer outro método, porque o tipo é selado.
No entanto, se você está feliz com as restrições que mencionei, então você poderia usar algo como o quadro Castelo DynamicProxy para criar um tipo que proxies o passado no objeto e intercepta as chamadas para a frente ou para re-implementar como apropriada.
Outras dicas
Bem, sim, é possível, mas envolve a geração de código na memória.
Você pode olhar para Reflection.Emit e aqui .
Note que vai envolver um monte de código.
Isto é, se eu supor que eu entendi corretamente.
Aqui está o que eu acho que você está pedindo:
SomeNameableObject a1 = new SomeNameableObject("ThisIsTheFirstName");
SomeNameableObject a2 = a1.Rename("ThisIsTheSecondName");
// a1 works, still has the name ThisIsTheFirstName
// a2 works, but everything is routed to a1,
// except for the name, which is ThisIsTheSecondName
Isso está correto?