Pergunta

Minha pergunta diz respeito a c# e como acessar membros estáticos ...Bem, eu realmente não sei como explicar isso (o que é ruim para uma pergunta, não é?) Vou apenas dar um exemplo de código:

Class test<T>{
     int method1(Obj Parameter1){
         //in here I want to do something which I would explain as
         T.TryParse(Parameter1);

         //my problem is that it does not work ... I get an error.
         //just to explain: if I declare test<int> (with type Integer)
         //I want my sample code to call int.TryParse(). If it were String
         //it should have been String.TryParse()
     }
}

Então, obrigado pessoal por suas respostas (a propósito, a pergunta é:como eu resolveria esse problema sem receber um erro).Esta provavelmente é uma pergunta muito fácil para você!

Obrigado, Niklas


Editar:Obrigado a todos por suas respostas!

Embora eu ache que a frase try-catch é a mais elegante, sei pela minha experiência com vb que pode ser realmente uma chatice.Usei-o uma vez e levei cerca de 30 minutos para executar um programa, que mais tarde levou apenas 2 minutos para ser computado só porque evitei o try-catch.

É por isso que escolhi a afirmação swich como a melhor resposta.Isso torna o código mais complicado, mas por outro lado imagino que seja relativamente rápido e relativamente fácil de ler.(Embora eu ainda ache que deveria haver uma maneira mais elegante...talvez na próxima língua que eu aprenda :P)


Porém, se você tiver alguma outra sugestão, ainda estou esperando (e disposto a participar)

Foi útil?

Solução

Mais uma forma de fazer isso, desta vez com alguma reflexão na mixagem:

static class Parser
{
    public static bool TryParse<TType>( string str, out TType x )
    {
        // Get the type on that TryParse shall be called
        Type objType = typeof( TType );

        // Enumerate the methods of TType
        foreach( MethodInfo mi in objType.GetMethods() )
        {
            if( mi.Name == "TryParse" )
            {
                // We found a TryParse method, check for the 2-parameter-signature
                ParameterInfo[] pi = mi.GetParameters();
                if( pi.Length == 2 ) // Find TryParse( String, TType )
                {
                    // Build a parameter list for the call
                    object[] paramList = new object[2] { str, default( TType ) };

                    // Invoke the static method
                    object ret = objType.InvokeMember( "TryParse", BindingFlags.InvokeMethod, null, null, paramList );

                    // Get the output value from the parameter list
                    x = (TType)paramList[1];
                    return (bool)ret;
                }
            }
        }

        // Maybe we should throw an exception here, because we were unable to find the TryParse
        // method; this is not just a unable-to-parse error.

        x = default( TType );
        return false;
    }
}

O próximo passo seria tentar implementar

public static TRet CallStaticMethod<TRet>( object obj, string methodName, params object[] args );

Com correspondência completa de tipo de parâmetro, etc.

Outras dicas

O problema é que TryParse não está definido em uma interface ou classe base em nenhum lugar, então você não pode presumir que o tipo passado para sua classe terá essa função.A menos que você possa contrair T de alguma forma, você encontrará muito isso.

Restrições em parâmetros de tipo

Resposta curta, você não pode.

Resposta longa, você pode trapacear:

public class Example
{
    internal static class Support
    {
        private delegate bool GenericParser<T>(string s, out T o);
        private static Dictionary<Type, object> parsers =
            MakeStandardParsers();
        private static Dictionary<Type, object> MakeStandardParsers()
        {
            Dictionary<Type, object> d = new Dictionary<Type, object>();
            // You need to add an entry for every type you want to cope with.
            d[typeof(int)] = new GenericParser<int>(int.TryParse);
            d[typeof(long)] = new GenericParser<long>(long.TryParse);
            d[typeof(float)] = new GenericParser<float>(float.TryParse);
            return d;
        }
        public static bool TryParse<T>(string s, out T result)
        {
            return ((GenericParser<T>)parsers[typeof(T)])(s, out result);
        }
    }
    public class Test<T>
    {
        public static T method1(string s)
        {
            T value;
            bool success = Support.TryParse(s, out value);
            return value;
        }
    }
    public static void Main()
    {
        Console.WriteLine(Test<int>.method1("23"));
        Console.WriteLine(Test<float>.method1("23.4"));
        Console.WriteLine(Test<long>.method1("99999999999999"));
        Console.ReadLine();
    }
}

Criei um dicionário estático contendo um delegado para o método TryParse de cada tipo que desejo usar.Em seguida, escrevi um método genérico para consultar o dicionário e passar a chamada ao delegado apropriado.Como cada delegado tem um tipo diferente, eu apenas os armazeno como referências de objeto e os devolvo ao tipo genérico apropriado quando os recupero.Observe que, para fins de exemplo simples, omiti a verificação de erros, como verificar se temos uma entrada no dicionário para o tipo determinado.

Para acessar um membro de uma classe ou interface específica, você precisa usar a palavra-chave Where e especificar a interface ou classe base que possui o método.

Na instância acima, TryParse não vem de uma interface ou classe base, então o que você está tentando fazer acima não é possível.Melhor apenas usar Convert.ChangeType e uma instrução try/catch.

class test<T>
{
    T Method(object P)
    {
       try {
           return (T)Convert.ChangeType(P, typeof(T));
       } catch(Exception e) {
           return null;
       }
    }
}

Você pretende fazer algo assim:

Class test<T>
{
     T method1(object Parameter1){

         if( Parameter1 is T ) 
         {
              T value = (T) Parameter1;
             //do something with value
             return value;
         }
         else
         {
             //Parameter1 is not a T
             return default(T); //or throw exception
         }
     }
}

Infelizmente você não pode verificar o padrão TryParse porque ele é estático - o que infelizmente significa que não é particularmente adequado para genéricos.

A única maneira de fazer exatamente o que você procura seria usar a reflexão para verificar se o método existe para T.

Outra opção é garantir que o objeto enviado seja um objeto conversível, restringindo o tipo para IConvertible (todos os tipos primitivos implementam IConvertible).Isso permitiria que você convertesse seu parâmetro para o tipo determinado com muita flexibilidade.

Class test<T>
{
    int method1(IConvertible Parameter1){

        IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T));

        T temp = Parameter1.ToType(typeof(T), provider);
    }
}

Você também pode fazer uma variação disso usando um tipo de 'objeto' como você fez originalmente.

Class test<T>
{
    int method1(object Parameter1){

        if(Parameter1 is IConvertible) {

            IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T));

            T temp = Parameter1.ToType(typeof(T), provider);

        } else {
           // Do something else
        }
    }
}

Ok pessoal:Obrigado por todos os peixes.Agora com suas respostas e minha pesquisa (especialmente o artigo sobre limitando tipos genéricos a primitivos) Vou apresentar-lhe a minha solução.

Class a<T>{
    private void checkWetherTypeIsOK()
    {
        if (T is int || T is float //|| ... any other types you want to be allowed){
            return true;
        }
        else {
            throw new exception();
        }
    }
    public static a(){
        ccheckWetherTypeIsOK();
    }
}

Esta não é realmente uma solução, mas em certos cenários pode ser uma boa alternativa:Podemos passar um delegado adicional para o método genérico.

Para esclarecer o que quero dizer, vamos usar um exemplo.Digamos que temos algum método de fábrica genérico, que deve criar uma instância de T, e queremos que ele chame outro método, para notificação ou inicialização adicional.

Considere a seguinte classe simples:

public class Example
{
    // ...

    public static void PostInitCallback(Example example)
    {
        // Do something with the object...
    }
}

E o seguinte método estático:

public static T CreateAndInit<T>() where T : new()
{
    var t = new T();
    // Some initialization code...
    return t;
}

Então agora teríamos que fazer:

var example = CreateAndInit<Example>();
Example.PostInitCallback(example);

No entanto, poderíamos mudar nosso método para receber um delegado adicional:

public delegate void PostInitCallback<T>(T t);
public static T CreateAndInit<T>(PostInitCallback<T> callback) where T : new()
{
    var t = new T();
    // Some initialization code...
    callback(t);
    return t;
}

E agora podemos mudar a chamada para:

var example = CreateAndInit<Example>(Example.PostInitCallback);

Obviamente, isso só é útil em cenários muito específicos.Mas esta é a solução mais limpa no sentido de que obtemos segurança no tempo de compilação, não há "hacking" envolvido e o código é extremamente simples.

Você provavelmente não consegue fazer isso.

Primeiro de tudo, se fosse possível, você precisaria de um limite mais rígido em T para que o verificador de tipos pudesse ter certeza de que todas as substituições possíveis para T realmente tinham um método estático chamado TryParse.

Você pode querer ler meu post anterior sobre limitando tipos genéricos a primitivos.Isso pode lhe dar algumas dicas para limitar o tipo que pode ser passado para o genérico (já que TipoParse obviamente está disponível apenas para um determinado número de primitivas ( string.TryParse obviamente sendo a exceção, o que não faz sentido).

Depois de ter mais controle sobre o tipo, você poderá tentar analisá-lo.Você pode precisar de uma mudança um pouco feia (para chamar o correto TryParse ), mas acho que você pode alcançar a funcionalidade desejada.

Se precisar que eu explique melhor alguma das opções acima, pergunte :)

Melhor código:restrinja T a ValueType desta forma:

class test1<T> where T: struct

Uma "estrutura" aqui significa um tipo de valor.String é uma classe, não um tipo de valor.int, float, Enums são todos tipos de valor.

aliás, o compilador não aceita chamar métodos estáticos ou acessar membros estáticos em 'parâmetros de tipo' como no exemplo a seguir, que não irá compilar :(

class MyStatic { public static int MyValue=0; }
class Test<T> where T: MyStatic
{
    public void TheTest() { T.MyValue++; }
}

=> Erro 1 'T' é um 'parâmetro de tipo', que não é válido no contexto fornecido

SL.

Não é assim que a estática funciona.Você tem que pensar na estática como uma espécie de classe Global, mesmo que ela esteja espalhada por vários tipos.Minha recomendação é torná-la uma propriedade dentro da instância T que possa acessar o método estático necessário.

Além disso, T é uma instância real de algo e, como qualquer outra instância, você não consegue acessar a estática desse tipo, por meio do valor instanciado.Aqui está um exemplo do que fazer:

class a {
    static StaticMethod1 ()
    virtual Method1 ()
}

class b : a {
    override Method1 () return StaticMethod1()
}

class c : a {
    override Method1 () return "XYZ"
}

class generic<T> 
    where T : a {
    void DoSomething () T.Method1()
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top